diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 45a44ca1..abdfaacb 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,7 +1,7 @@
{
- "name": "agentfs sandbox",
+ "name": "vfs sandbox",
"dockerComposeFile": ["docker-compose.yml"],
- "service": "agentfs",
+ "service": "vfs",
"remoteUser": "node",
"workspaceFolder": "/workspace",
"waitFor": "postStartCommand",
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index 390bf127..c668dafc 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -14,7 +14,7 @@ services:
timeout: 5s
retries: 10
start_period: 1s
- agentfs:
+ vfs:
depends_on:
proxy:
condition: service_healthy
diff --git a/.github/assets/agentfs-arch.svg b/.github/assets/vfs-arch.svg
similarity index 99%
rename from .github/assets/agentfs-arch.svg
rename to .github/assets/vfs-arch.svg
index b49bdc1d..d41b4774 100644
--- a/.github/assets/agentfs-arch.svg
+++ b/.github/assets/vfs-arch.svg
@@ -1,4 +1,4 @@
\ No newline at end of file
+ @font-face { font-family: Excalifont; src: url(data:font/woff2;base64,d09GMgABAAAAABLAAA4AAAAAICQAABJrAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbh2wcNAZgAIEsEQgKrgiiCQs8AAE2AiQDdAQgBYMYByAb+BijoorVTvYXB7Yx68AfYEjTmIYYlh+dSFw0liENZXDxw0aYPMCZ17LKXUO0zft/oqVLxEIEo6aUgFiYhd2rcG7tOsNFhUT0h/6+e4loaWSZgMRrAQlIRGJhiVD5v//fnNW+c1nLqM4BK1ZY0qfUxM63MIGWA0sEmeRPXb12a02qPEmGhaOlww8MhkC1yaRM0f9r91oZD8hwQJP7gCBbkzLTp/8B/Pv73+/X6orHDc0QKwVCs1LJ+7jY4WL2dvEkrokIpVE3opK+JMRD2aGTIrEQI6FRSuMVbsvA/M0oxuHE6Nf+gQACAAnsAWAQhBC05HkItAxQo3FcKuCvOptqAH/XVFoN+OeiljrAhwIAeG4MOG2a6gCODxC2wdUCMzbghBuEM4P4deGvPEbDAPreBhntu1Cfvm53bvU230sw7SE79FZvsKD8wqwFxESA3nyuQ20tL4zqKzTPgsCvCNTFW1JNfccS57MPz9IrXOPbZdnxn+dtkHrfpuiybHkryHRiADBlsO9W3RA6+N13Chw8IhIyCrcbdAAmDq4mSJhgCSBklgsMk4m3+kAaNASKAWznIDQcKjI8Aq+BCrxiSaoEhEzWMQaiAkiYAaxy0xwXedgglxoEfxBFkN5ux4EsNQIArpATQQnaNWLqsWC8i0BHp+cpiQ0gT9dp3JLUMvkgJ2VsCgCIVbuGpnR+79hsONYysSWAQ6Q6a9ufhB4MKG0dABY24ms7JdJnCCMXnRG2hwlllpST1oBtbb5MLKziJEmVrlipcpXqtercRcLNRk8spxRrkbtG03e477HrjjjskH02bVg3a8a0KRMCjSAQD/oHYC4RhNkGVRLsHcA96MTNvgUAK4bGc4O8Xgnkkv2X1jLFLD5Gfj5euWjt39u9e/vVywe2Q53ubIGtl2oXjmuVnUd+P3/WuVmfou55heAkvcF0K8GTqrUH09vEwJs3/5aEh1u9eWLqrLR6vM9TJDHw4UPAjyDl8kj+hRhRYn1z0zCnyXSjbWxRzbCZAGozXCIEieu6mBjgHgTMJZoMA8A/4E52J8toW6+cSyOHGQ7otqUtCxKkS6TeCBGc/InKtlkUoqroqCI7Hc/zSCCnTZ/qvX7+Ks9b2qxxExnWV/WWBW5GTpi9yR6sG7EdvC7qcgfmWID+Te2EJKHnYv+2PVfsOrO1lY2HnQGV3UFjPh5rZKhVEl6LCjldFhVepq2kKNZ0FwFuSbD+z9JDuguDhWCB3WV34YTzHV9OOMJRT6iSO8vF40OxbRC2c1VbGtuqgGZribDM8ouX5fPrYve1Q9kdBBgeqRQAklaPxCbeVBCaJVEG/NVFBF0Jgpo/CcjB433yLhijzbycqvVhGz1QThz7s3cXsKsKE/W1XlNUG05+s6F2h7MqlqxS0VeOCe8709eTy9PUv6dN7nnLHj58Cg+Dv5OD9OwRpnu+vAHofHTclgVEp+JdwfNqfSRxKsIwRz1SoesItKhU5YDBJNBOH0EjOU1+4iaYEteVUlZf9r7ceFTDDsiU4TzxR2OqEfKRznY+K4VuADN7Nnjfyq6TPgQMQrU6RIwFsPN7KBoq87OMqlHCS2lK1oq6TBngBCDXmuD/Djmzne86K0bwESJF1vn+ezJVVMti4NlR1eXz6whq2tKmxVURrmJ9VYQM/o/RdQY8ASK/NeEpwx0IW/QMK3+cLtvxuTVFNvGm+804ZFgpOqj/jOyAbMnOMl1vUWvzJOSisa87BYnttaV+p9fdD3eQWs4eZB+o+vCBft/rhoE0U5epwnEMHoeoL2Xfy7G2FktFvuOi7Q2zIGZBzflRnUm8I8NgY8eVwUZyOnHUnYxCkjRAOCRetShkGBkEnT55UzEkDDPFwpqhMi+nsImMsbZ3W+eKU1TKhc7RGikb8zVy3z/E2qIcq02aOMlASMNb2FZRwoeQmeCqmJAQ0PKKfvYiLxqlFfFkv6L7CCxKXGhJMDHLZ7Gw9HYx4SzSGtkRt6Fxffi4ak5r9/t+XBmcGJpsn9ixHVfz46JX+T5VCMpFrkbW9O5zBGdX85K9sdCM7KTXfV4PFHkBGUb3hguRhU9/kjc7t0l8bM6pvDi8L3aG9xZbdqhaIGOPlAnfhpWvg2IixPZZP2/k3UVJ7Flx6uJ44rRl+DTvrvETniLPjQZSLT+9en4sOx1qqZ0dw3xXZMeOj8hNxsZQ+MRw8dM6dxea7X+9t4N0SS83pm/FR3y2WAMabgrqCuNEkae2ikpD7N9GcFSAUCThU8HdlSYCsq19JB+B+GIku5v8oRYyydXILG4MzvTh75i+J8GU4T6oke+6ijAdpAkoxP1ThIuCOIa7kzQxGc5iMZEB7QLrWXJXU9Gc3BDVuuhxEbSEi7OuVOVcsvPJSY0ArRWdIzm9QsUi7gi6j6vT+nbNH0pNC2O+sdpatKzJyawZYxI0GjXlzcmOggvQBM0HQJ3GwEsJy2mDITKxvbFWEvfwlB1cUuRshHPVqn+WXUFNnWVzGYNmS2JCUuvrVC0v0+ndKll1NS1lxaRkA8bW2e37a5W8N0q3nW0E+VG9JTLxZvzaASGqHEXeEokVCR7gAmyoGExKtIr935f3758+3zspCZhQZEqMleb4CmB3dFWjkAUMsGx6rOemEm2G21AJJlcSVt4fp6cH6dat5A2ChhonwV13IC9FpLz/l3AgGTvJ91dqypfsZhwDBngzb2lJmCfFvrX2Wj//M2XEvW7FIR7Im+Qm1szT9MOd023y9M5JjXI8kr3Jdr66VZoA7yYHuaP5WhshhihpKACl39KtKVfrmIGZhY9n8GZbpinhyDvVpVFmfKsl7zFx5iIhKznNvWqZqltoT09rXpGqQ5S4CAYSp2/63ng/pNwET5YEFG/k5VIT/+2QOTk977iiesAV2b9lO6PzwqggwJMqNquW/aimsuXx9Sv0Hxeocd+nIYLW5JEMCyTkUORbkLFAteReWXmNNmeNiGP/wmH+2ITtLVy0xHU063HZ0CnsQo/JpyyApydvf9MSQ7osJMxIg6ZZutmzmUTSE4xuT0nJSuoqBcN5NT/je+virwt8o82/mLnQcKi+YRNzL8BWuinEGAiCXfzZFJjec3S0O0SEVj/X6uNpMWhcKmPO6eWYb5gGqan55rYSrQZkaH7JzPe0WQ3zgRmYDHEWDDYabZC1NsSdORjBxo/vRI5QDhGRCcCKqiuRsK4REDruUc8xsy9aDz8wxkJOXRO5M5thk3yrScVYRsTkGsVUFc1E3xOSOndXg8WEdpKp+cOmqN8rdSyquYzZ2W5D5Pi5POqcECGzBBng+DEMnpqsPZbMixrNsrbmhP2OasET7fBtECP7hNvdVXJtC7zJ2Zi8dSsGTWv9nV1OsMIEDu6IDX8MAoo4jtNjS+h9AXHpT7O0W0XpTkqzJlsVht0Ft22PrtwuPN5rlJTQxrsp263vBiwklEXq6NY0LDrJ35FR3JVTJ8RagvZBxkgoG2VixNQ5VPiUfaCVuiSOickjHf7jwBxcLnO0JWOlV7a6cZdzBmSE3zi2ZdfKVGcX+YK1eCI8fPa5qPgSfCGjEA/XihQWLJEXxPN/+6KQ0ninS+aOFNKwz9Iw+WexgvWrwHyV88jNVfc7LtC2GxMjDB7dmFumhVeSF4pgwTp4GWE2Og6KsrSNcHZHcVJz7BvGvA8Y9t4vejUvNh378oiziqniNo94t+GJXHkWZc5MT4n9Q50sW+OzkNV2tYcKflLxXZTNpbgeO0rzLkarzA7sYDn8vfN9cJgC6NVMXyJjKdmx4m3mV888IQOfVfdaKYOyMRmLksclDwI0QdlgPFKtAWWMPF/IRMh2CCbGPYdT1Ls7Q0IC+eTtafPSMPMwtQyXcpK3NZ+1NRZkWH8+Oc0tqTcpJ+i5ceEcICVjomJ7YLC5m95Me2L4drSNMwoQgTUpYiNNOcVvKcr4OFDxCJD4LpggAG/HC1dodObDB58CBCX9ejmN4um+ctNJN+M7TYUvRlZWgYsJrtxS4p7F5oXuSNWNVKex7QP0lYXEFS53Y8pPhOGTdOwN/Yvz8md7Ga2xSImwZ3hs9KNbv09xBVacNS/Yrjipzg/Pf3maIgYYL13XKgNvdVplRimw5KQEAx7cDCfIV67JUVA1j1Uz/aE1X1UGyIF/PXlmSrR/Wq3HgAi3m/M3NTq10ABk16NxQ22gacpv0+Hpri4DET25C86FnIwK7YaBAdvjhQTM4bmxkubJgVEJo/K/GSNqS8FmnllfCiWgGajTGkqu1mmhJkKDG8V9w3tZbhGi3y7PmSmcXKwBXToKWdRARTcQ7NBjB8obY5VJcNBsasPiBtJyEgNteGJAv8ZmgEs4An7OEoQ7RL6XehwI+SWjt8d0jNN19mxRju4QvEkK1v4Y9rNw2z7rcUi+Rl5aJU6/cLqio+iQOJ7tiCE/hbcP+fx5D5zmExi7Zm/urnWIE1br/4EjmCdGrmpC1a3ENJYX5BW926dE3mN3S8TSKueEnMvNmchJ4MhzuBMoiag/AO3tWmU9qgxYzTZ6mBtTTXm0m9DAUjvSgQ1woPRTM7c2oCr5gEAssXk6NPhcKGUQJlXAek9mVd2jzSQIP6LdRgvdxiPOocqXYvscicYpD2kYMzMuwD8rnXqy/6mTqTTb19cLfJSJ+IPWB2lnadkgl3yIn/S5mHr3N8ZaN2k4u8DjpeGkrOxylPvJQ8iVb6FXpr/q+aQO2Aubwpd90f2Jh7ZUo5sCsNyOPOCRfkxZVhWZFMf9XHyoMSlKhUrKQtgsfc2D05SVrrgRjGEn75qb3OXM3i4nOykRZfK0KLJft6ObvxkJIjjlqX5iI4t5Vle+GDDbGNbg7Nq5Bl5CEYe+1zLlhxwBd4bj4/PpqCR4YpB/+OgwOyEws+JHQrANoIlaIrp9R7Tz3+dqq9wSHh6zpmwP7a5SamIWSHBt85GVXUogCJmXKKmktEtL5t7zeQkmHe79JZISVgwvYtCcydMN3br+ik4olJZBG809twyQRQKsE5uJ5YteqY5fZNXWX/XFyf0g2M+zCgxjcE4+gAZxyVgC8RQfJl7w6WQLJfAQNGIjkH3Ju7HTYyAkWrpxKbn/SdAfLCtULoC/hLFJPTkFgfP+PMn03PWNGdzM6Jfy+NbGaY5DuCwo46BororUfiS1r25mrO9AuBjvn2LpoM1PnK+PezcVvheVQWyG/UFQnL5gHNgKE3j0snK6MVe1wc6RY3AyY3BgMkOT0LjYsk8SQ5wuQscjeKr5o/RolTclGDJvua5Ir1fcMciWVk2u3iK2pdGTkMMQ/7W0AtxDeVUeiPmDqqzyqtxgVM+KcnRHndRb10fVDhJ+K23QSmPV6eyYECvRuKgckqdqfCTH99BSeyw4D28iRKj9uKUaTFJMpViY6M1yEkcvJ0NLF3GWj88ZXsBE4/kT7EzSKtugMQSG+1jQxCaPDw1f/vhyPwoSg7BhCzBGM667h+jeiJZtNhCFZ6YGtvkuiDel8Y2BVtcVAab2Ra4yBQ3LkE/+Wy1tdlbyKHTSx22tf25eG/Xjw9a0T5nBpvxeLK1RNngwYIZcznG/ssOdOEDFtKebj9zCiza77j4yhzIySYdCyndVsJdpPJCGMnf/O5uNl5evBcOKflipmzl5VrcQQuJG/v/SAO0X51bjyOj2yoE2VCtzw6HL95MMhQOC452N0sHgDgmDDTOOeTmklJsMRw7olEiXbekK1K/q4ULRxDQyRvOdIfcfZkOkGljpHuaXfNGlPooX7XOUCR3qkPPrsQiqcClF5uetCkH1I9s+/6VngQfN0unuMlJ+6MR1/aZcuWUNlfGKrwtyaoxFBDtnqJr2Y6gN4/0RKaT42C71n/3oZsbf0MRylCViQspArlAe3z+yMSDgPRw2bOBuipzVvQnDR/b5BGOPAbqV82v0QYFbxBOHkJcAAI8vXhZ1Ru/9zYC+kR+Aet0IBYxbOYxsc1Bpk4449nd+w8tdiKpLdJkMQDeMLAPgT8RKAuRM55lSmkjGyvYwVgAUqYOvWaA4sWOgirfA8tNtDo/AVuvtK6rHAjJTRCggNJm0ZR7/SKjjyYHSDtHpdsAnchOGjGUUVhvARVjZEFADDwCocWUcBWE6EAWj2BSF8NIVhaLkikKLpAQm0ABAtA7FitSoVKZenRa+kpmvjK0oCpE+JZs0o1nBQCl/GtqODGfDWzs1qHDEUqMeOgV1bdTtHMyz9WSkc1sL3yKFTYJw1txgYRRmJRt0IqgkcYUQ1AR4Kqmj7SEgly6d+RwuSsgBV7bTZB6jGjWz+Nw8hVJlLEVbgBJSEWIGTvM/NAAA); }SQLite Database FilePagePagePagePagePageFilesystemKey-ValueToolcallsB-TreeVfs SDK
\ No newline at end of file
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 89453d5e..78d14672 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -97,7 +97,7 @@ jobs:
- name: Allow unprivileged user namespaces
run: |
# Ubuntu 24.04 runner images gate unprivileged user namespaces behind
- # AppArmor; without this the agentfs run suites SKIP and the strict
+ # AppArmor; without this the vfs run suites SKIP and the strict
# gate goes red instead of exercising the sandbox for real.
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
@@ -120,10 +120,10 @@ jobs:
# fails the strict gate. The corruption-torture uring leg needs no
# entry: with enable_uring=N the mount falls back to the legacy
# channel and the leg passes.
- AGENTFS_GATE_ALLOWED_SKIPS: fuse-sigint-panic-census
+ VFS_GATE_ALLOWED_SKIPS: fuse-sigint-panic-census
run: |
- # scripts/gate.sh runs crates/agentfs-cli/tests/all.sh with
- # AGENTFS_GATE_STRICT=1, then phase8-validation.py --smoke (which
+ # scripts/gate.sh runs crates/vfs-cli/tests/all.sh with
+ # VFS_GATE_STRICT=1, then phase8-validation.py --smoke (which
# includes the noopen/flush/base-drift coherence harnesses) and the
# consistency-canon census.
scripts/gate.sh
@@ -131,7 +131,7 @@ jobs:
- name: Run pjdfstest phase5-ci
run: |
scripts/validation/posix/run-pjdfstest.sh \
- --agentfs-bin "$PWD/target/release/agentfs" \
+ --vfs-bin "$PWD/target/release/vfs" \
--pjdfstest-dir /tmp/pjdfstest \
--profile phase5-ci
diff --git a/.gitignore b/.gitignore
index cd8a225e..1f14afde 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,7 @@
__MACOSX/
.AppleDouble
.LSOverride
-Icon[
-]
+Icon?
# Thumbnails
._*
@@ -26,6 +25,7 @@ Network Trash Folder
Temporary Items
.apdisk
+.vfs
.agentfs
target/
diff --git a/Cargo.lock b/Cargo.lock
index ae3fcbc3..305decd2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -47,110 +47,6 @@ dependencies = [
"subtle",
]
-[[package]]
-name = "agentfs-cli"
-version = "0.6.4"
-dependencies = [
- "agentfs-core",
- "agentfs-mount",
- "anyhow",
- "base64",
- "chrono",
- "clap",
- "clap_complete",
- "dirs",
- "hex",
- "libc",
- "parking_lot",
- "serde",
- "serde_json",
- "sha1",
- "tempfile",
- "tokio",
- "tracing",
- "tracing-subscriber",
- "turso",
- "uuid",
-]
-
-[[package]]
-name = "agentfs-core"
-version = "0.6.4"
-dependencies = [
- "aegis",
- "async-trait",
- "criterion",
- "libc",
- "lru",
- "parking_lot",
- "proptest",
- "rand 0.8.6",
- "serde",
- "serde_json",
- "smallvec",
- "tempfile",
- "thiserror 1.0.69",
- "tokio",
- "tracing",
- "turso",
-]
-
-[[package]]
-name = "agentfs-fuse"
-version = "0.6.4"
-dependencies = [
- "agentfs-core",
- "anyhow",
- "async-trait",
- "libc",
- "memchr",
- "nix",
- "page_size",
- "parking_lot",
- "smallvec",
- "tempfile",
- "tokio",
- "tracing",
- "turso",
- "zerocopy",
-]
-
-[[package]]
-name = "agentfs-mount"
-version = "0.6.4"
-dependencies = [
- "agentfs-core",
- "agentfs-fuse",
- "agentfs-nfs",
- "anyhow",
- "async-trait",
- "libc",
- "tempfile",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
-[[package]]
-name = "agentfs-nfs"
-version = "0.6.4"
-dependencies = [
- "agentfs-core",
- "anyhow",
- "async-trait",
- "byteorder",
- "filetime",
- "libc",
- "num-derive",
- "num-traits",
- "smallvec",
- "tempfile",
- "tokio",
- "tokio-util",
- "tracing",
- "uuid",
-]
-
[[package]]
name = "aho-corasick"
version = "1.1.4"
@@ -2942,6 +2838,110 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+[[package]]
+name = "vfs-cli"
+version = "0.6.4"
+dependencies = [
+ "anyhow",
+ "base64",
+ "chrono",
+ "clap",
+ "clap_complete",
+ "dirs",
+ "hex",
+ "libc",
+ "parking_lot",
+ "serde",
+ "serde_json",
+ "sha1",
+ "tempfile",
+ "tokio",
+ "tracing",
+ "tracing-subscriber",
+ "turso",
+ "uuid",
+ "vfs-core",
+ "vfs-mount",
+]
+
+[[package]]
+name = "vfs-core"
+version = "0.6.4"
+dependencies = [
+ "aegis",
+ "async-trait",
+ "criterion",
+ "libc",
+ "lru",
+ "parking_lot",
+ "proptest",
+ "rand 0.8.6",
+ "serde",
+ "serde_json",
+ "smallvec",
+ "tempfile",
+ "thiserror 1.0.69",
+ "tokio",
+ "tracing",
+ "turso",
+]
+
+[[package]]
+name = "vfs-fuse"
+version = "0.6.4"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "libc",
+ "memchr",
+ "nix",
+ "page_size",
+ "parking_lot",
+ "smallvec",
+ "tempfile",
+ "tokio",
+ "tracing",
+ "turso",
+ "vfs-core",
+ "zerocopy",
+]
+
+[[package]]
+name = "vfs-mount"
+version = "0.6.4"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "libc",
+ "tempfile",
+ "tokio",
+ "tokio-util",
+ "tracing",
+ "vfs-core",
+ "vfs-fuse",
+ "vfs-nfs",
+]
+
+[[package]]
+name = "vfs-nfs"
+version = "0.6.4"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "byteorder",
+ "filetime",
+ "libc",
+ "num-derive",
+ "num-traits",
+ "smallvec",
+ "tempfile",
+ "tokio",
+ "tokio-util",
+ "tracing",
+ "uuid",
+ "vfs-core",
+]
+
[[package]]
name = "wait-timeout"
version = "0.2.1"
diff --git a/Cargo.toml b/Cargo.toml
index b822a0b9..da6ec3da 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,10 +1,10 @@
[workspace]
members = [
- "crates/agentfs-cli",
- "crates/agentfs-core",
- "crates/agentfs-fuse",
- "crates/agentfs-nfs",
- "crates/agentfs-mount",
+ "crates/vfs-cli",
+ "crates/vfs-core",
+ "crates/vfs-fuse",
+ "crates/vfs-nfs",
+ "crates/vfs-mount",
]
resolver = "2"
@@ -12,7 +12,7 @@ resolver = "2"
version = "0.6.4"
edition = "2021"
license = "MIT"
-repository = "https://github.com/tursodatabase/agentfs"
+repository = "https://github.com/tursodatabase/vfs"
# Concurrency canon (architecture.md section 7 item 7): no lock guard held
# across an .await; enforced structurally, not by review.
@@ -20,11 +20,11 @@ repository = "https://github.com/tursodatabase/agentfs"
await_holding_lock = "deny"
[workspace.dependencies]
-agentfs-core = { path = "crates/agentfs-core" }
-agentfs-cli = { path = "crates/agentfs-cli" }
-agentfs-fuse = { path = "crates/agentfs-fuse" }
-agentfs-mount = { path = "crates/agentfs-mount" }
-agentfs-nfs = { path = "crates/agentfs-nfs" }
+vfs-core = { path = "crates/vfs-core" }
+vfs-cli = { path = "crates/vfs-cli" }
+vfs-fuse = { path = "crates/vfs-fuse" }
+vfs-mount = { path = "crates/vfs-mount" }
+vfs-nfs = { path = "crates/vfs-nfs" }
anyhow = "1.0"
async-trait = "0.1"
diff --git a/README.md b/README.md
index a8cf8b48..fdfb1f40 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
-# AgentFS
+# Vfs
The filesystem for agents: a SQLite-backed virtual filesystem with
copy-on-write sandboxing, mountable over FUSE (Linux) or NFS.
> **⚠️ Warning:** This software is in BETA. It may still contain bugs and unexpected behavior. Use caution with production data and ensure you have backups.
-## What is AgentFS?
+## What is Vfs?
-AgentFS stores everything an agent does — every file it creates, every piece
+Vfs stores everything an agent does — every file it creates, every piece
of key-value state, every tool call it records — in a single SQLite database
file. That gives agent state three properties ordinary filesystems don't
have:
@@ -18,9 +18,9 @@ have:
* **Portability**: the entire agent runtime (files, state, history) moves
between machines as a single `.db` file.
-For sandboxed coding-agent workloads, AgentFS layers that database over a
+For sandboxed coding-agent workloads, Vfs layers that database over a
read-only view of the host filesystem: reads are scoped to the configured
-base tree, writes go only to the AgentFS delta database, and the real
+base tree, writes go only to the Vfs delta database, and the real
filesystem is never modified. This holds even against prompt-injected agents:
the isolation is enforced at the filesystem layer, not by convention.
@@ -30,15 +30,15 @@ This repository is one Cargo workspace with five crates:
| Crate | Role |
|---|---|
-| `crates/agentfs-core` | The engine: storage, overlay/copy-on-write, schema authority, typed config, telemetry, semantics (access/durability/handles). The only crate meant for external consumption. |
-| `crates/agentfs-fuse` | Sealed Linux FUSE mount surface (transport + adapter). |
-| `crates/agentfs-nfs` | Sealed NFSv3 serve surface (transport + adapter). |
-| `crates/agentfs-mount` | One mount lifecycle: `mount_fs`, `MountHandle`, supervision, daemonize. |
-| `crates/agentfs-cli` | The `agentfs` binary: thin CLI edge over the crates above. |
+| `crates/vfs-core` | The engine: storage, overlay/copy-on-write, schema authority, typed config, telemetry, semantics (access/durability/handles). The only crate meant for external consumption. |
+| `crates/vfs-fuse` | Sealed Linux FUSE mount surface (transport + adapter). |
+| `crates/vfs-nfs` | Sealed NFSv3 serve surface (transport + adapter). |
+| `crates/vfs-mount` | One mount lifecycle: `mount_fs`, `MountHandle`, supervision, daemonize. |
+| `crates/vfs-cli` | The `vfs` binary: thin CLI edge over the crates above. |
-Platform support: **Linux is first-tier** (FUSE and NFS backends, `agentfs
+Platform support: **Linux is first-tier** (FUSE and NFS backends, `vfs
run` sandbox, full validation gate). **macOS is second-tier**: NFS mount
-plus a sandboxed `agentfs run` (Seatbelt/`sandbox-exec` with default-deny
+plus a sandboxed `vfs run` (Seatbelt/`sandbox-exec` with default-deny
read scoping), validated by a manual release gate
(`scripts/validation/macos-nfs-git-validation.sh`) run on real hardware —
see [docs/TESTING.md](docs/TESTING.md). No other platforms are supported.
@@ -49,35 +49,35 @@ Build and install the CLI from source:
```bash
cargo +nightly build --release --workspace --bins
-install -m 0755 target/release/agentfs ~/.local/bin/
+install -m 0755 target/release/vfs ~/.local/bin/
```
Initialize an agent filesystem:
```bash
-$ agentfs init my-agent
-Created agent filesystem: .agentfs/my-agent.db
+$ vfs init my-agent
+Created agent filesystem: .vfs/my-agent.db
Agent ID: my-agent
```
Inspect it without mounting:
```bash
-$ agentfs fs my-agent ls
+$ vfs fs my-agent ls
f hello.txt
-$ agentfs fs my-agent cat hello.txt
+$ vfs fs my-agent cat hello.txt
hello from agent
# Or address the database file directly
-$ agentfs fs .agentfs/my-agent.db cat hello.txt
+$ vfs fs .vfs/my-agent.db cat hello.txt
hello from agent
```
View the agent's tool-call timeline:
```bash
-$ agentfs timeline my-agent
+$ vfs timeline my-agent
ID TOOL STATUS DURATION STARTED
4 execute_code pending -- 2024-01-05 09:44:20
3 api_call error 300ms 2024-01-05 09:44:15
@@ -88,7 +88,7 @@ ID TOOL STATUS DURATION STARTED
Mount it as a real filesystem (FUSE on Linux, NFS on macOS):
```bash
-$ agentfs mount my-agent ./mnt
+$ vfs mount my-agent ./mnt
$ echo "hello" > ./mnt/hello.txt
$ cat ./mnt/hello.txt
hello
@@ -98,42 +98,42 @@ Run a program in a copy-on-write sandbox over your current directory
(FUSE + user/mount namespaces on Linux):
```bash
-$ agentfs run --session my-session -- bash
+$ vfs run --session my-session -- bash
# ... work normally; every write lands in the delta database,
# the host filesystem is untouched ...
$ exit
# List live sandbox sessions; inspect what a session changed
-$ agentfs ps
-$ agentfs diff my-session
+$ vfs ps
+$ vfs diff my-session
```
Beyond these basics, the CLI covers the rest of the lifecycle:
-* `agentfs exec` — run a one-shot command over a temporary mount, then
+* `vfs exec` — run a one-shot command over a temporary mount, then
unmount automatically.
-* `agentfs clone` — bulk-ingest a git repository straight into the database.
-* `agentfs sync` — push/pull the database to a remote Turso (libSQL) server.
-* `agentfs serve nfs` / `agentfs serve mcp` — export the filesystem over
+* `vfs clone` — bulk-ingest a git repository straight into the database.
+* `vfs sync` — push/pull the database to a remote Turso (libSQL) server.
+* `vfs serve nfs` / `vfs serve mcp` — export the filesystem over
NFS, or expose filesystem and KV-store tools to agents over MCP.
* `--key` / `--cipher` — local at-rest encryption of the database.
-* `agentfs backup`, `integrity`, `migrate`, `materialize`, `prune` —
+* `vfs backup`, `integrity`, `migrate`, `materialize`, `prune` —
portable backups, corruption checks, schema migration, partial-origin
materialization, and mount cleanup.
The **[User Manual](docs/MANUAL.md)** documents every command; its command
reference is generated from the CLI's own argument definitions, so it cannot
-drift from `agentfs --help`.
+drift from `vfs --help`.
-## Using AgentFS as a library
+## Using Vfs as a library
-The `agentfs-core` crate provides programmatic access to the same engine the
+The `vfs-core` crate provides programmatic access to the same engine the
CLI uses: filesystem, key-value store, and tool-call audit trail over a
-single database. See the crate rustdoc (`cargo doc -p agentfs-core`).
+single database. See the crate rustdoc (`cargo doc -p vfs-core`).
## How it works
-At the heart of AgentFS is the [agent filesystem](docs/SPEC.md), a complete
+At the heart of Vfs is the [agent filesystem](docs/SPEC.md), a complete
SQLite-based storage system implemented on
[Turso](https://github.com/tursodatabase/turso). The database schema
separates namespace (dentries) from data (inodes + chunked/inline content),
@@ -151,17 +151,17 @@ declared in the generated [docs/KNOBS.md](docs/KNOBS.md) ledger.
## FAQ
-### How is AgentFS different from _X_?
+### How is Vfs different from _X_?
-[Bubblewrap](https://github.com/containers/bubblewrap) provides filesystem isolation using Linux namespaces and overlays. While you could achieve similar isolation with a `bwrap` call that mounts `/` read-only and uses `--tmp-overlay` on the working directory, the key difference is persistence and queryability: with AgentFS, the upper filesystem is stored in a single SQLite database file, which you can query, snapshot, and move to another machine.
+[Bubblewrap](https://github.com/containers/bubblewrap) provides filesystem isolation using Linux namespaces and overlays. While you could achieve similar isolation with a `bwrap` call that mounts `/` read-only and uses `--tmp-overlay` on the working directory, the key difference is persistence and queryability: with Vfs, the upper filesystem is stored in a single SQLite database file, which you can query, snapshot, and move to another machine.
-[Docker Sandbox](https://www.docker.com/blog/docker-sandboxes-a-new-approach-for-coding-agent-safety/) and AgentFS are complementary rather than competing. AgentFS answers "what happened and what's the state?" while Docker Sandboxes answer "how do I run this safely?" You could use both together: run an agent inside a Docker Sandbox for security, while using AgentFS inside that sandbox for structured state management and audit trails.
+[Docker Sandbox](https://www.docker.com/blog/docker-sandboxes-a-new-approach-for-coding-agent-safety/) and Vfs are complementary rather than competing. Vfs answers "what happened and what's the state?" while Docker Sandboxes answer "how do I run this safely?" You could use both together: run an agent inside a Docker Sandbox for security, while using Vfs inside that sandbox for structured state management and audit trails.
-[Git worktrees](https://git-scm.com/docs/git-worktree) let you check out multiple branches of a repository into separate directories, allowing agents to work on independent copies of the source code — similar to AgentFS. But AgentFS solves the problem at a lower level. With git worktrees, nothing prevents an agent from modifying files outside its worktree: another agent's worktree, system files, or anything else on the filesystem. The isolation is purely conventional, not enforced. AgentFS provides filesystem-level copy-on-write isolation that cannot be bypassed, and because it operates below git, it also handles untracked files.
+[Git worktrees](https://git-scm.com/docs/git-worktree) let you check out multiple branches of a repository into separate directories, allowing agents to work on independent copies of the source code — similar to Vfs. But Vfs solves the problem at a lower level. With git worktrees, nothing prevents an agent from modifying files outside its worktree: another agent's worktree, system files, or anything else on the filesystem. The isolation is purely conventional, not enforced. Vfs provides filesystem-level copy-on-write isolation that cannot be bypassed, and because it operates below git, it also handles untracked files.
-### Why implement AgentFS at the filesystem layer instead of using containers or VMs?
+### Why implement Vfs at the filesystem layer instead of using containers or VMs?
-The filesystem layer gives us capabilities that block devices can't. First, because everything is stored in structured SQLite tables, you can query the filesystem, which is essential for auditability and debugging agent behavior. Second, SQLite's write-ahead log enables snapshotting and time-travel forking by capturing every filesystem change. Third, the engine works in environments where mounting a block device is impossible. This approach also composes with containers and VMs: AgentFS is reachable over remote filesystem protocols like NFS or through mechanisms like virtio-fuse.
+The filesystem layer gives us capabilities that block devices can't. First, because everything is stored in structured SQLite tables, you can query the filesystem, which is essential for auditability and debugging agent behavior. Second, SQLite's write-ahead log enables snapshotting and time-travel forking by capturing every filesystem change. Third, the engine works in environments where mounting a block device is impossible. This approach also composes with containers and VMs: Vfs is reachable over remote filesystem protocols like NFS or through mechanisms like virtio-fuse.
## Learn more
@@ -170,7 +170,7 @@ The filesystem layer gives us capabilities that block devices can't. First, beca
- **[Runtime Knobs](docs/KNOBS.md)** - generated ledger of every tunable
- **[Testing](docs/TESTING.md)** - validation gates, benchmark policy, and the manual macOS release gate
- **[CHANGELOG](CHANGELOG.md)** - including the fork-era restructure summary
-- **[Turso database](https://github.com/tursodatabase/turso)** - the in-process SQL database AgentFS builds on
+- **[Turso database](https://github.com/tursodatabase/turso)** - the in-process SQL database Vfs builds on
## License
diff --git a/crates/agentfs-cli/scripts/mount.fuse.agentfs b/crates/agentfs-cli/scripts/mount.fuse.agentfs
deleted file mode 100755
index d3df14df..00000000
--- a/crates/agentfs-cli/scripts/mount.fuse.agentfs
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-# Determine the path to agentfs
-if [ -n "$AGENTFS_BIN" ]; then
- AGENTFS_PATH="$AGENTFS_BIN"
-else
- AGENTFS_PATH="$(which agentfs 2>/dev/null)"
- if [ -z "$AGENTFS_PATH" ]; then
- echo "Error: agentfs binary not found. Please install agentfs or set AGENTFS_BIN." >&2
- exit 1
- fi
-fi
-
-"$AGENTFS_PATH" mount "$1" "$2"
diff --git a/crates/agentfs-core/.gitignore b/crates/agentfs-core/.gitignore
deleted file mode 100644
index db7d859d..00000000
--- a/crates/agentfs-core/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-target
-
-# AgentFS local databases
-.agentfs/
diff --git a/crates/agentfs-cli/.gitignore b/crates/vfs-cli/.gitignore
similarity index 100%
rename from crates/agentfs-cli/.gitignore
rename to crates/vfs-cli/.gitignore
diff --git a/crates/agentfs-cli/CONTRIBUTING.md b/crates/vfs-cli/CONTRIBUTING.md
similarity index 63%
rename from crates/agentfs-cli/CONTRIBUTING.md
rename to crates/vfs-cli/CONTRIBUTING.md
index d1b8a2cf..12c1f539 100644
--- a/crates/agentfs-cli/CONTRIBUTING.md
+++ b/crates/vfs-cli/CONTRIBUTING.md
@@ -1,4 +1,4 @@
-# AgentFS Contributor Guide
+# Vfs Contributor Guide
## Running Tests
diff --git a/crates/agentfs-cli/Cargo.toml b/crates/vfs-cli/Cargo.toml
similarity index 83%
rename from crates/agentfs-cli/Cargo.toml
rename to crates/vfs-cli/Cargo.toml
index 7f909cdc..2368bc08 100644
--- a/crates/agentfs-cli/Cargo.toml
+++ b/crates/vfs-cli/Cargo.toml
@@ -1,17 +1,17 @@
[package]
-name = "agentfs-cli"
+name = "vfs-cli"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
-description = "Command-line interface for AgentFS"
+description = "Command-line interface for Vfs"
[lib]
-name = "agentfs_cli"
+name = "vfs_cli"
path = "src/lib.rs"
[[bin]]
-name = "agentfs"
+name = "vfs"
path = "src/main.rs"
[features]
@@ -22,8 +22,8 @@ strict = []
workspace = true
[dependencies]
-agentfs-core = { workspace = true }
-agentfs-mount = { workspace = true }
+vfs-core = { workspace = true }
+vfs-mount = { workspace = true }
tokio = { workspace = true }
clap = { workspace = true }
anyhow = { workspace = true }
diff --git a/crates/agentfs-cli/build.rs b/crates/vfs-cli/build.rs
similarity index 92%
rename from crates/agentfs-cli/build.rs
rename to crates/vfs-cli/build.rs
index 091b2c1c..69427f1d 100644
--- a/crates/agentfs-cli/build.rs
+++ b/crates/vfs-cli/build.rs
@@ -5,7 +5,7 @@ fn main() {
let repo_root = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(Path::parent)
- .expect("agentfs-cli should live under crates/agentfs-cli");
+ .expect("vfs-cli should live under crates/vfs-cli");
// Capture git version from tags for --version flag
// Rerun if git HEAD changes (new commits or tags)
@@ -46,5 +46,5 @@ fn main() {
.map(|v| v.trim().to_string())
.unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string());
- println!("cargo:rustc-env=AGENTFS_VERSION={}", version);
+ println!("cargo:rustc-env=VFS_VERSION={}", version);
}
diff --git a/crates/agentfs-cli/perf/simulate-npm-workload.sh b/crates/vfs-cli/perf/simulate-npm-workload.sh
similarity index 99%
rename from crates/agentfs-cli/perf/simulate-npm-workload.sh
rename to crates/vfs-cli/perf/simulate-npm-workload.sh
index 17211b9e..766f9c92 100755
--- a/crates/agentfs-cli/perf/simulate-npm-workload.sh
+++ b/crates/vfs-cli/perf/simulate-npm-workload.sh
@@ -1,7 +1,7 @@
#!/bin/bash
#
# Simulates a workload similar to `npx create-react-app` for testing
-# AgentFS/FUSE performance.
+# Vfs/FUSE performance.
#
# Based on observed FUSE operation distribution:
# lookup: 18090 (~52%) - path resolution
diff --git a/crates/agentfs-cli/perf/syscall/Makefile b/crates/vfs-cli/perf/syscall/Makefile
similarity index 100%
rename from crates/agentfs-cli/perf/syscall/Makefile
rename to crates/vfs-cli/perf/syscall/Makefile
diff --git a/crates/agentfs-cli/perf/syscall/hello.txt b/crates/vfs-cli/perf/syscall/hello.txt
similarity index 100%
rename from crates/agentfs-cli/perf/syscall/hello.txt
rename to crates/vfs-cli/perf/syscall/hello.txt
diff --git a/crates/agentfs-cli/perf/syscall/perf-open-close.c b/crates/vfs-cli/perf/syscall/perf-open-close.c
similarity index 100%
rename from crates/agentfs-cli/perf/syscall/perf-open-close.c
rename to crates/vfs-cli/perf/syscall/perf-open-close.c
diff --git a/crates/agentfs-cli/perf/syscall/perf-statx.c b/crates/vfs-cli/perf/syscall/perf-statx.c
similarity index 100%
rename from crates/agentfs-cli/perf/syscall/perf-statx.c
rename to crates/vfs-cli/perf/syscall/perf-statx.c
diff --git a/crates/agentfs-cli/perf/syscall/run.sh b/crates/vfs-cli/perf/syscall/run.sh
similarity index 58%
rename from crates/agentfs-cli/perf/syscall/run.sh
rename to crates/vfs-cli/perf/syscall/run.sh
index 5b208ca5..994baf1f 100755
--- a/crates/agentfs-cli/perf/syscall/run.sh
+++ b/crates/vfs-cli/perf/syscall/run.sh
@@ -2,8 +2,8 @@
#
# Benchmark syscall performance across different scenarios:
# 1. Native filesystem
-# 2. AgentFS (file in base layer)
-# 3. AgentFS (file copied up to delta layer)
+# 2. Vfs (file in base layer)
+# 3. Vfs (file copied up to delta layer)
#
set -e
@@ -13,14 +13,14 @@ CLI_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
REPO_ROOT="$(cd "$CLI_DIR/../.." && pwd)"
TEST_FILE="$SCRIPT_DIR/hello.txt"
ITERATIONS="${1:-100000}"
-AGENTFS="$REPO_ROOT/target/release/agentfs"
+VFS="$REPO_ROOT/target/release/vfs"
# Build benchmarks if needed
make -C "$SCRIPT_DIR" -s
-# Check agentfs binary
-if [ ! -x "$AGENTFS" ]; then
- echo "Error: agentfs binary not found at $AGENTFS"
+# Check vfs binary
+if [ ! -x "$VFS" ]; then
+ echo "Error: vfs binary not found at $VFS"
echo "Run: cargo build --release"
exit 1
fi
@@ -51,24 +51,24 @@ run_benchmark() {
NATIVE_LATENCY=$(echo "$NATIVE_OUTPUT" | extract_latency)
NATIVE_THROUGHPUT=$(echo "$NATIVE_OUTPUT" | extract_throughput)
- # Test 2: AgentFS (file in base layer)
- echo "[2/3] AgentFS (base layer)..."
- AGENTFS_BASE_OUTPUT=$("$AGENTFS" run "$benchmark" "$TEST_FILE" "$ITERATIONS" 2>&1)
- AGENTFS_BASE_LATENCY=$(echo "$AGENTFS_BASE_OUTPUT" | extract_latency)
- AGENTFS_BASE_THROUGHPUT=$(echo "$AGENTFS_BASE_OUTPUT" | extract_throughput)
+ # Test 2: Vfs (file in base layer)
+ echo "[2/3] Vfs (base layer)..."
+ VFS_BASE_OUTPUT=$("$VFS" run "$benchmark" "$TEST_FILE" "$ITERATIONS" 2>&1)
+ VFS_BASE_LATENCY=$(echo "$VFS_BASE_OUTPUT" | extract_latency)
+ VFS_BASE_THROUGHPUT=$(echo "$VFS_BASE_OUTPUT" | extract_throughput)
- # Test 3: AgentFS (file copied up to delta)
- echo "[3/3] AgentFS (delta layer)..."
- AGENTFS_DELTA_OUTPUT=$("$AGENTFS" run sh -c "
+ # Test 3: Vfs (file copied up to delta)
+ echo "[3/3] Vfs (delta layer)..."
+ VFS_DELTA_OUTPUT=$("$VFS" run sh -c "
touch '$TEST_FILE'
'$benchmark' '$TEST_FILE' '$ITERATIONS'
" 2>&1)
- AGENTFS_DELTA_LATENCY=$(echo "$AGENTFS_DELTA_OUTPUT" | extract_latency)
- AGENTFS_DELTA_THROUGHPUT=$(echo "$AGENTFS_DELTA_OUTPUT" | extract_throughput)
+ VFS_DELTA_LATENCY=$(echo "$VFS_DELTA_OUTPUT" | extract_latency)
+ VFS_DELTA_THROUGHPUT=$(echo "$VFS_DELTA_OUTPUT" | extract_throughput)
# Calculate overhead percentages
- AGENTFS_BASE_OVERHEAD=$(echo "scale=1; (($AGENTFS_BASE_LATENCY / $NATIVE_LATENCY) - 1) * 100" | bc)
- AGENTFS_DELTA_OVERHEAD=$(echo "scale=1; (($AGENTFS_DELTA_LATENCY / $NATIVE_LATENCY) - 1) * 100" | bc)
+ VFS_BASE_OVERHEAD=$(echo "scale=1; (($VFS_BASE_LATENCY / $NATIVE_LATENCY) - 1) * 100" | bc)
+ VFS_DELTA_OVERHEAD=$(echo "scale=1; (($VFS_DELTA_LATENCY / $NATIVE_LATENCY) - 1) * 100" | bc)
# Results
echo ""
@@ -77,8 +77,8 @@ run_benchmark() {
printf "%-25s %10s %12s %10s\n" "Scenario" "Latency" "Throughput" "Overhead"
printf "%-25s %10s %12s %10s\n" "--------" "-------" "----------" "--------"
printf "%-25s %8s ns %10s/s %10s\n" "Native" "$NATIVE_LATENCY" "$NATIVE_THROUGHPUT" "-"
- printf "%-25s %8s ns %10s/s %8s %%\n" "AgentFS (base)" "$AGENTFS_BASE_LATENCY" "$AGENTFS_BASE_THROUGHPUT" "$AGENTFS_BASE_OVERHEAD"
- printf "%-25s %8s ns %10s/s %8s %%\n" "AgentFS (delta)" "$AGENTFS_DELTA_LATENCY" "$AGENTFS_DELTA_THROUGHPUT" "$AGENTFS_DELTA_OVERHEAD"
+ printf "%-25s %8s ns %10s/s %8s %%\n" "Vfs (base)" "$VFS_BASE_LATENCY" "$VFS_BASE_THROUGHPUT" "$VFS_BASE_OVERHEAD"
+ printf "%-25s %8s ns %10s/s %8s %%\n" "Vfs (delta)" "$VFS_DELTA_LATENCY" "$VFS_DELTA_THROUGHPUT" "$VFS_DELTA_OVERHEAD"
echo "----------------------------------------------"
echo ""
}
diff --git a/crates/agentfs-cli/scripts/fmt-imports b/crates/vfs-cli/scripts/fmt-imports
similarity index 100%
rename from crates/agentfs-cli/scripts/fmt-imports
rename to crates/vfs-cli/scripts/fmt-imports
diff --git a/crates/vfs-cli/scripts/mount.fuse.vfs b/crates/vfs-cli/scripts/mount.fuse.vfs
new file mode 100755
index 00000000..4d7d7a67
--- /dev/null
+++ b/crates/vfs-cli/scripts/mount.fuse.vfs
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# Determine the path to vfs
+if [ -n "$VFS_BIN" ]; then
+ VFS_PATH="$VFS_BIN"
+else
+ VFS_PATH="$(which vfs 2>/dev/null)"
+ if [ -z "$VFS_PATH" ]; then
+ echo "Error: vfs binary not found. Please install vfs or set VFS_BIN." >&2
+ exit 1
+ fi
+fi
+
+"$VFS_PATH" mount "$1" "$2"
diff --git a/crates/agentfs-cli/src/cmd/clone.rs b/crates/vfs-cli/src/cmd/clone.rs
similarity index 95%
rename from crates/agentfs-cli/src/cmd/clone.rs
rename to crates/vfs-cli/src/cmd/clone.rs
index ce8a2933..e97d4aec 100644
--- a/crates/agentfs-cli/src/cmd/clone.rs
+++ b/crates/vfs-cli/src/cmd/clone.rs
@@ -1,4 +1,4 @@
-//! `agentfs clone`: populate an AgentFS database from a git repository
+//! `vfs clone`: populate a Vfs database from a git repository
//! without per-file FUSE round trips.
//!
//! A regular `git clone` through the mount pays ~9-11 FUSE round trips plus
@@ -6,7 +6,7 @@
//! `git clone --no-checkout` through a temporary mount (pack files are a few
//! large sequential writes), then streams the worktree content out of the
//! object database: a producer thread parses `git ls-tree` + `git cat-file
-//! --batch` output while an [`agentfs_core::ImportSession`] consumer bulk
+//! --batch` output while an [`vfs_core::ImportSession`] consumer bulk
//! imports each chunk (large multi-inode transactions), so blob decoding
//! overlaps SQLite writes instead of buffering every blob in memory first.
//! Finally it fabricates a git index whose cached stat data matches exactly
@@ -25,17 +25,17 @@ use std::process::{Command, Stdio};
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
-use agentfs_core::{AgentFSOptions, FileSystem, ImportEntry, ImportOptions, ImportedEntry};
use anyhow::{bail, Context, Result};
use sha1::{Digest, Sha1};
+use vfs_core::{FileSystem, ImportEntry, ImportOptions, ImportedEntry, VfsOptions};
-use crate::cmd::init::open_agentfs;
+use crate::cmd::init::open_vfs;
use crate::opts::MountBackend;
-use agentfs_mount::supervise::{
+use vfs_mount::supervise::{
run_supervised_task, set_parent_death_signal_std, supervise_command, ChildOutcome,
SupervisedTaskOutcome,
};
-use agentfs_mount::{mount_fs, MountOpts};
+use vfs_mount::{mount_fs, MountOpts};
const S_IFDIR: u32 = 0o040000;
const MODE_FILE: u32 = 0o100644;
@@ -65,22 +65,22 @@ pub async fn handle_clone_command(
None => derive_repo_name(&source)?,
};
- let options = AgentFSOptions::resolve(&id_or_path)
- .unwrap_or_else(|_| AgentFSOptions::with_path(&id_or_path));
- let agentfs = open_agentfs(options)
+ let options =
+ VfsOptions::resolve(&id_or_path).unwrap_or_else(|_| VfsOptions::with_path(&id_or_path));
+ let vfs = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
- let agent = agentfs.fs.clone();
- let fs: Arc = Arc::new(agentfs.fs);
+ let agent = vfs.fs.clone();
+ let fs: Arc = Arc::new(vfs.fs);
let clone_id = uuid::Uuid::new_v4().to_string();
- let mountpoint = std::env::temp_dir().join(format!("agentfs-clone-{clone_id}"));
+ let mountpoint = std::env::temp_dir().join(format!("vfs-clone-{clone_id}"));
std::fs::create_dir_all(&mountpoint).context("failed to create mount directory")?;
let mount_opts = MountOpts {
mountpoint: mountpoint.clone(),
backend: backend.into(),
- fsname: format!("agentfs:{id_or_path}"),
+ fsname: format!("vfs:{id_or_path}"),
uid: None,
gid: None,
allow_other: false,
@@ -136,7 +136,7 @@ struct CloneSummary {
}
async fn clone_into_mount(
- agent: &agentfs_core::fs::AgentFS,
+ agent: &vfs_core::fs::Vfs,
mountpoint: &Path,
source: &str,
repo_name: &str,
@@ -314,10 +314,10 @@ fn ls_tree(repo: &Path) -> Result> {
let _objtype = fields.next().context("missing object type")?;
let sha = fields.next().context("missing object id")?;
if mode == MODE_GITLINK {
- bail!("repository contains submodules ({path}); not supported by agentfs clone");
+ bail!("repository contains submodules ({path}); not supported by vfs clone");
}
if sha.len() != 40 {
- bail!("non-SHA-1 repository detected; not supported by agentfs clone");
+ bail!("non-SHA-1 repository detected; not supported by vfs clone");
}
rows.push(TreeRow {
mode,
diff --git a/crates/agentfs-cli/src/cmd/completions.rs b/crates/vfs-cli/src/cmd/completions.rs
similarity index 95%
rename from crates/agentfs-cli/src/cmd/completions.rs
rename to crates/vfs-cli/src/cmd/completions.rs
index 559b6e44..7e506203 100644
--- a/crates/agentfs-cli/src/cmd/completions.rs
+++ b/crates/vfs-cli/src/cmd/completions.rs
@@ -62,11 +62,11 @@ impl Shell {
/// Get the completion source line for this shell
fn completion_line(&self) -> &'static str {
match self {
- Shell::Bash => "source <(COMPLETE=bash agentfs)",
- Shell::Zsh => "source <(COMPLETE=zsh agentfs)",
- Shell::Fish => "COMPLETE=fish agentfs | source",
- Shell::Elvish => "eval (COMPLETE=elvish agentfs | slurp)",
- Shell::PowerShell => "$env:COMPLETE = \"powershell\"; agentfs | Out-String | Invoke-Expression; Remove-Item Env:\\COMPLETE",
+ Shell::Bash => "source <(COMPLETE=bash vfs)",
+ Shell::Zsh => "source <(COMPLETE=zsh vfs)",
+ Shell::Fish => "COMPLETE=fish vfs | source",
+ Shell::Elvish => "eval (COMPLETE=elvish vfs | slurp)",
+ Shell::PowerShell => "$env:COMPLETE = \"powershell\"; vfs | Out-String | Invoke-Expression; Remove-Item Env:\\COMPLETE",
}
}
}
diff --git a/crates/agentfs-cli/src/cmd/exec.rs b/crates/vfs-cli/src/cmd/exec.rs
similarity index 81%
rename from crates/agentfs-cli/src/cmd/exec.rs
rename to crates/vfs-cli/src/cmd/exec.rs
index 5822b27a..4bb0d9d7 100644
--- a/crates/agentfs-cli/src/cmd/exec.rs
+++ b/crates/vfs-cli/src/cmd/exec.rs
@@ -1,23 +1,23 @@
-//! Execute a command with an AgentFS filesystem mounted.
+//! Execute a command with a Vfs filesystem mounted.
//!
-//! This module provides the `agentfs exec` command which mounts an AgentFS
+//! This module provides the `vfs exec` command which mounts a Vfs
//! filesystem to a temporary directory, runs a command with that as the
//! working directory, and automatically unmounts when done.
-use agentfs_core::{AgentFSOptions, EncryptionConfig, FileSystem, HostFS, OverlayFS};
use anyhow::{Context, Result};
use std::path::PathBuf;
use std::sync::Arc;
use turso::value::Value;
+use vfs_core::{EncryptionConfig, FileSystem, HostFS, OverlayFS, VfsOptions};
-use crate::cmd::init::open_agentfs;
+use crate::cmd::init::open_vfs;
use crate::opts::MountBackend;
-use agentfs_mount::supervise::{exit_code_for_spawn_error, exit_code_for_status, run_supervised};
-use agentfs_mount::{mount_fs, MountOpts};
+use vfs_mount::supervise::{exit_code_for_spawn_error, exit_code_for_status, run_supervised};
+use vfs_mount::{mount_fs, MountOpts};
/// Handle the exec command.
///
-/// Mounts the specified AgentFS, runs the command, and unmounts on completion.
+/// Mounts the specified Vfs, runs the command, and unmounts on completion.
pub async fn handle_exec_command(
id_or_path: String,
command: PathBuf,
@@ -25,8 +25,8 @@ pub async fn handle_exec_command(
backend: MountBackend,
encryption: Option<(String, String)>,
) -> Result<()> {
- // Resolve AgentFS options
- let mut opts = AgentFSOptions::resolve(&id_or_path)?;
+ // Resolve Vfs options
+ let mut opts = VfsOptions::resolve(&id_or_path)?;
if let Some((key, cipher)) = encryption {
opts = opts.with_encryption(EncryptionConfig {
hex_key: key,
@@ -34,14 +34,14 @@ pub async fn handle_exec_command(
});
}
- // Open AgentFS
- let agentfs = open_agentfs(opts)
+ // Open Vfs
+ let vfs = open_vfs(opts)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
// Check for overlay configuration
let fs: Arc = {
- let conn = agentfs.get_connection().await?;
+ let conn = vfs.get_connection().await?;
// Check if fs_overlay_config table exists and has base_path
let query = "SELECT value FROM fs_overlay_config WHERE key = 'base_path'";
@@ -65,21 +65,21 @@ pub async fn handle_exec_command(
if let Some(base_path) = base_path {
eprintln!("Using overlay filesystem with base: {}", base_path);
let hostfs = HostFS::new(&base_path)?;
- let overlay = OverlayFS::new(Arc::new(hostfs), agentfs.fs);
+ let overlay = OverlayFS::new(Arc::new(hostfs), vfs.fs);
overlay.load().await?; // Load persisted whiteouts and origin mappings
Arc::new(overlay) as Arc
} else {
- Arc::new(agentfs.fs) as Arc
+ Arc::new(vfs.fs) as Arc
}
};
// Create a temporary directory for the mount
let exec_id = uuid::Uuid::new_v4().to_string();
- let mountpoint = std::env::temp_dir().join(format!("agentfs-exec-{}", exec_id));
+ let mountpoint = std::env::temp_dir().join(format!("vfs-exec-{}", exec_id));
std::fs::create_dir_all(&mountpoint).context("Failed to create mount directory")?;
let fsname = format!(
- "agentfs:{}",
+ "vfs:{}",
std::fs::canonicalize(&id_or_path)
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| id_or_path.clone())
diff --git a/crates/agentfs-cli/src/cmd/fs.rs b/crates/vfs-cli/src/cmd/fs.rs
similarity index 77%
rename from crates/agentfs-cli/src/cmd/fs.rs
rename to crates/vfs-cli/src/cmd/fs.rs
index 1f915680..3c1af9ad 100644
--- a/crates/agentfs-cli/src/cmd/fs.rs
+++ b/crates/vfs-cli/src/cmd/fs.rs
@@ -1,10 +1,10 @@
use std::collections::VecDeque;
-use agentfs_core::{AgentFSOptions, EncryptionConfig};
use anyhow::{Context, Result as AnyhowResult};
use turso::Value;
+use vfs_core::{EncryptionConfig, VfsOptions};
-use crate::cmd::init::{finalize_readonly, open_agentfs};
+use crate::cmd::init::{finalize_readonly, open_vfs};
const ROOT_INO: i64 = 1;
const S_IFMT: u32 = 0o170000;
@@ -18,7 +18,7 @@ pub async fn ls_filesystem(
path: &str,
encryption: Option<&(String, String)>,
) -> AnyhowResult<()> {
- let mut options = AgentFSOptions::resolve(&id_or_path)?;
+ let mut options = VfsOptions::resolve(&id_or_path)?;
if let Some((key, cipher)) = encryption {
options = options.with_encryption(EncryptionConfig {
hex_key: key.clone(),
@@ -27,20 +27,20 @@ pub async fn ls_filesystem(
}
eprintln!("Using agent: {}", id_or_path);
- let agentfs = open_agentfs(options)
+ let vfs = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
- let result = ls_opened(stdout, &agentfs, path).await;
- finalize_readonly(&agentfs).await;
+ let result = ls_opened(stdout, &vfs, path).await;
+ finalize_readonly(&vfs).await;
result
}
async fn ls_opened(
stdout: &mut impl std::io::Write,
- agentfs: &agentfs_core::AgentFS,
+ vfs: &vfs_core::Vfs,
path: &str,
) -> AnyhowResult<()> {
- let conn = agentfs.get_connection().await?;
+ let conn = vfs.get_connection().await?;
let start_ino = resolve_directory_ino(&conn, path).await?;
@@ -154,19 +154,19 @@ pub async fn cat_filesystem(
path: &str,
encryption: Option<&(String, String)>,
) -> AnyhowResult<()> {
- let mut options = AgentFSOptions::resolve(&id_or_path)?;
+ let mut options = VfsOptions::resolve(&id_or_path)?;
if let Some((key, cipher)) = encryption {
options = options.with_encryption(EncryptionConfig {
hex_key: key.clone(),
cipher: cipher.clone(),
});
}
- let agentfs = open_agentfs(options)
+ let vfs = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
let result: AnyhowResult<()> = async {
- match agentfs.fs.read_file(path).await? {
+ match vfs.fs.read_file(path).await? {
Some(file) => {
stdout.write_all(&file)?;
Ok(())
@@ -175,7 +175,7 @@ pub async fn cat_filesystem(
}
}
.await;
- finalize_readonly(&agentfs).await;
+ finalize_readonly(&vfs).await;
result
}
@@ -185,19 +185,19 @@ pub async fn write_filesystem(
content: &str,
encryption: Option<&(String, String)>,
) -> AnyhowResult<()> {
- let mut options = AgentFSOptions::resolve(&id_or_path)?;
+ let mut options = VfsOptions::resolve(&id_or_path)?;
if let Some((key, cipher)) = encryption {
options = options.with_encryption(EncryptionConfig {
hex_key: key.clone(),
cipher: cipher.clone(),
});
}
- let agentfs = open_agentfs(options)
+ let vfs = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
// Own created entries as the invoking user, matching mount-created files:
- // uid/gid 0 would make a later chmod inside `agentfs exec` (running as the
+ // uid/gid 0 would make a later chmod inside `vfs exec` (running as the
// invoking uid) fail with EPERM.
// SAFETY: getuid/getgid are always safe
let (uid, gid) = unsafe { (libc::getuid(), libc::getgid()) };
@@ -209,26 +209,23 @@ pub async fn write_filesystem(
// we must start with /a (first TWO entries)
for i in 2..components.len() {
let dir_path = components[0..i].join("/");
- if agentfs.fs.stat(&dir_path).await?.is_none() {
- agentfs.fs.mkdir(&dir_path, uid, gid).await?;
+ if vfs.fs.stat(&dir_path).await?.is_none() {
+ vfs.fs.mkdir(&dir_path, uid, gid).await?;
}
}
// Remove file if it exists (overwrite behavior)
- if agentfs.fs.stat(path).await?.is_some() {
- agentfs.fs.remove(path).await?;
+ if vfs.fs.stat(path).await?.is_some() {
+ vfs.fs.remove(path).await?;
}
- let (_, file) = agentfs
- .fs
- .create_file(path, S_IFREG | 0o644, uid, gid)
- .await?;
+ let (_, file) = vfs.fs.create_file(path, S_IFREG | 0o644, uid, gid).await?;
file.pwrite(0, content.as_bytes()).await?;
// Tier Four: writes go into the in-memory batcher first. This CLI is a
// one-shot operation — finalize so the bytes are durable in SQLite before
- // we drop the AgentFS (otherwise a subsequent process or `cat` against
+ // we drop the Vfs (otherwise a subsequent process or `cat` against
// the same path would see only the pre-write state) and the database
// family is single-file again (invariant I1) instead of keeping the
// truncated 0-byte -wal a bare drain leaves behind.
- agentfs.fs.finalize().await?;
+ vfs.fs.finalize().await?;
Ok(())
}
@@ -267,10 +264,10 @@ fn path_exists_in_base(base_path: &str, rel_path: &str) -> bool {
}
pub async fn diff_filesystem(id_or_path: String) -> AnyhowResult<()> {
- let options = AgentFSOptions::resolve(&id_or_path)?;
+ let options = VfsOptions::resolve(&id_or_path)?;
eprintln!("Using agent: {}", id_or_path);
- let agent = open_agentfs(options)
+ let agent = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
let result = diff_opened(&agent).await;
@@ -278,7 +275,7 @@ pub async fn diff_filesystem(id_or_path: String) -> AnyhowResult<()> {
result
}
-async fn diff_opened(agent: &agentfs_core::AgentFS) -> AnyhowResult<()> {
+async fn diff_opened(agent: &vfs_core::Vfs) -> AnyhowResult<()> {
// Check if overlay is enabled
let base_path = match agent.is_overlay_enabled().await? {
Some(path) => path,
@@ -348,27 +345,27 @@ async fn diff_opened(agent: &agentfs_core::AgentFS) -> AnyhowResult<()> {
#[cfg(test)]
mod tests {
- use agentfs_core::{AgentFS, AgentFSOptions, EncryptionConfig};
use tempfile::NamedTempFile;
+ use vfs_core::{EncryptionConfig, Vfs, VfsOptions};
use crate::cmd::fs::{cat_filesystem, ls_filesystem, write_filesystem};
const TEST_KEY: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
const TEST_CIPHER: &str = "aes256gcm";
- async fn agentfs() -> (AgentFS, String, NamedTempFile) {
+ async fn vfs() -> (Vfs, String, NamedTempFile) {
let file = NamedTempFile::new().unwrap();
let path = file.path().to_str().unwrap();
- let agentfs = AgentFS::open(AgentFSOptions::with_path(path.to_string()))
+ let vfs = Vfs::open(VfsOptions::with_path(path.to_string()))
.await
.unwrap();
- (agentfs, file.path().to_str().unwrap().to_string(), file)
+ (vfs, file.path().to_str().unwrap().to_string(), file)
}
- async fn encrypted_agentfs() -> (AgentFS, String, NamedTempFile) {
+ async fn encrypted_vfs() -> (Vfs, String, NamedTempFile) {
let file = NamedTempFile::new().unwrap();
let path = file.path().to_str().unwrap();
- let agentfs = AgentFS::open(AgentFSOptions::with_path(path.to_string()).with_encryption(
+ let vfs = Vfs::open(VfsOptions::with_path(path.to_string()).with_encryption(
EncryptionConfig {
hex_key: TEST_KEY.to_string(),
cipher: TEST_CIPHER.to_string(),
@@ -376,14 +373,14 @@ mod tests {
))
.await
.unwrap();
- (agentfs, file.path().to_str().unwrap().to_string(), file)
+ (vfs, file.path().to_str().unwrap().to_string(), file)
}
const S_IFREG: u32 = 0o100000;
#[tokio::test]
async fn cat_file_not_found() {
- let (_agentfs, path, _file) = agentfs().await;
+ let (_vfs, path, _file) = vfs().await;
let mut buf = Vec::new();
let err = cat_filesystem(&mut buf, path, "test.md", None)
.await
@@ -393,11 +390,9 @@ mod tests {
#[tokio::test]
async fn cat_file_found() {
- let (agentfs, path, _file) = agentfs().await;
- let content = b"hello, agentfs";
- write_file(&agentfs.fs, "test.md", content, 0, 0)
- .await
- .unwrap();
+ let (vfs, path, _file) = vfs().await;
+ let content = b"hello, vfs";
+ write_file(&vfs.fs, "test.md", content, 0, 0).await.unwrap();
let mut buf = Vec::new();
cat_filesystem(&mut buf, path, "test.md", None)
.await
@@ -407,9 +402,9 @@ mod tests {
#[tokio::test]
async fn cat_big_file_found() {
- let (agentfs, path, _file) = agentfs().await;
+ let (vfs, path, _file) = vfs().await;
let content = vec![100u8; 4 * 1024 * 1024];
- write_file(&agentfs.fs, "test.md", &content, 0, 0)
+ write_file(&vfs.fs, "test.md", &content, 0, 0)
.await
.unwrap();
let mut buf = Vec::new();
@@ -421,7 +416,7 @@ mod tests {
#[tokio::test]
async fn ls_empty() {
- let (_agentfs, path, _file) = agentfs().await;
+ let (_vfs, path, _file) = vfs().await;
let mut buf = Vec::new();
ls_filesystem(&mut buf, path, "/", None).await.unwrap();
assert_eq!(buf, b"");
@@ -429,11 +424,11 @@ mod tests {
#[tokio::test]
async fn ls_files_only() {
- let (agentfs, path, _file) = agentfs().await;
- write_file(&agentfs.fs, "1.md", b"1", 0, 0).await.unwrap();
- write_file(&agentfs.fs, "2.md", b"11", 0, 0).await.unwrap();
+ let (vfs, path, _file) = vfs().await;
+ write_file(&vfs.fs, "1.md", b"1", 0, 0).await.unwrap();
+ write_file(&vfs.fs, "2.md", b"11", 0, 0).await.unwrap();
let big = vec![100u8; 1024 * 1024];
- write_file(&agentfs.fs, "3.md", &big, 0, 0).await.unwrap();
+ write_file(&vfs.fs, "3.md", &big, 0, 0).await.unwrap();
let mut buf = Vec::new();
ls_filesystem(&mut buf, path, "/", None).await.unwrap();
assert_eq!(
@@ -447,22 +442,16 @@ f 3.md
#[tokio::test]
async fn ls_dirs() {
- let (agentfs, path, _file) = agentfs().await;
- agentfs.fs.mkdir("a", 0, 0).await.unwrap();
- agentfs.fs.mkdir("a/b", 0, 0).await.unwrap();
- agentfs.fs.mkdir("a/c", 0, 0).await.unwrap();
- agentfs.fs.mkdir("d", 0, 0).await.unwrap();
- agentfs.fs.mkdir("d/e", 0, 0).await.unwrap();
- write_file(&agentfs.fs, "a/b/1.md", b"1", 0, 0)
- .await
- .unwrap();
- write_file(&agentfs.fs, "a/c/2.md", b"11", 0, 0)
- .await
- .unwrap();
+ let (vfs, path, _file) = vfs().await;
+ vfs.fs.mkdir("a", 0, 0).await.unwrap();
+ vfs.fs.mkdir("a/b", 0, 0).await.unwrap();
+ vfs.fs.mkdir("a/c", 0, 0).await.unwrap();
+ vfs.fs.mkdir("d", 0, 0).await.unwrap();
+ vfs.fs.mkdir("d/e", 0, 0).await.unwrap();
+ write_file(&vfs.fs, "a/b/1.md", b"1", 0, 0).await.unwrap();
+ write_file(&vfs.fs, "a/c/2.md", b"11", 0, 0).await.unwrap();
let big = vec![100u8; 1024 * 1024];
- write_file(&agentfs.fs, "d/e/3.md", &big, 0, 0)
- .await
- .unwrap();
+ write_file(&vfs.fs, "d/e/3.md", &big, 0, 0).await.unwrap();
let mut buf = Vec::new();
ls_filesystem(&mut buf, path, "/", None).await.unwrap();
assert_eq!(
@@ -481,20 +470,14 @@ f d/e/3.md
#[tokio::test]
async fn ls_subdir_lists_only_subtree() {
- let (agentfs, path, _file) = agentfs().await;
- agentfs.fs.mkdir("a", 0, 0).await.unwrap();
- agentfs.fs.mkdir("a/b", 0, 0).await.unwrap();
- agentfs.fs.mkdir("a/c", 0, 0).await.unwrap();
- agentfs.fs.mkdir("d", 0, 0).await.unwrap();
- write_file(&agentfs.fs, "a/b/1.md", b"1", 0, 0)
- .await
- .unwrap();
- write_file(&agentfs.fs, "a/c/2.md", b"11", 0, 0)
- .await
- .unwrap();
- write_file(&agentfs.fs, "d/3.md", b"111", 0, 0)
- .await
- .unwrap();
+ let (vfs, path, _file) = vfs().await;
+ vfs.fs.mkdir("a", 0, 0).await.unwrap();
+ vfs.fs.mkdir("a/b", 0, 0).await.unwrap();
+ vfs.fs.mkdir("a/c", 0, 0).await.unwrap();
+ vfs.fs.mkdir("d", 0, 0).await.unwrap();
+ write_file(&vfs.fs, "a/b/1.md", b"1", 0, 0).await.unwrap();
+ write_file(&vfs.fs, "a/c/2.md", b"11", 0, 0).await.unwrap();
+ write_file(&vfs.fs, "d/3.md", b"111", 0, 0).await.unwrap();
let mut buf = Vec::new();
ls_filesystem(&mut buf, path.clone(), "/a", None)
.await
@@ -518,7 +501,7 @@ f c/2.md
#[tokio::test]
async fn ls_missing_path_errors() {
- let (_agentfs, path, _file) = agentfs().await;
+ let (_vfs, path, _file) = vfs().await;
let mut buf = Vec::new();
let err = ls_filesystem(&mut buf, path, "/missing", None)
.await
@@ -531,10 +514,8 @@ f c/2.md
#[tokio::test]
async fn ls_file_path_errors() {
- let (agentfs, path, _file) = agentfs().await;
- write_file(&agentfs.fs, "file.md", b"1", 0, 0)
- .await
- .unwrap();
+ let (vfs, path, _file) = vfs().await;
+ write_file(&vfs.fs, "file.md", b"1", 0, 0).await.unwrap();
let mut buf = Vec::new();
let err = ls_filesystem(&mut buf, path, "/file.md", None)
.await
@@ -547,11 +528,9 @@ f c/2.md
#[tokio::test]
async fn read_commands_leave_no_wal_sidecar() {
- let (agentfs, path, _file) = agentfs().await;
- write_file(&agentfs.fs, "test.md", b"1", 0, 0)
- .await
- .unwrap();
- drop(agentfs);
+ let (vfs, path, _file) = vfs().await;
+ write_file(&vfs.fs, "test.md", b"1", 0, 0).await.unwrap();
+ drop(vfs);
let wal = format!("{path}-wal");
let mut buf = Vec::new();
@@ -586,12 +565,12 @@ f c/2.md
#[tokio::test]
async fn encrypted_write_and_cat() {
- let (agentfs, path, _file) = encrypted_agentfs().await;
+ let (vfs, path, _file) = encrypted_vfs().await;
let content = b"encrypted content";
- write_file(&agentfs.fs, "secret.txt", content, 0, 0)
+ write_file(&vfs.fs, "secret.txt", content, 0, 0)
.await
.unwrap();
- drop(agentfs);
+ drop(vfs);
let encryption = Some((TEST_KEY.to_string(), TEST_CIPHER.to_string()));
let mut buf = Vec::new();
@@ -603,14 +582,10 @@ f c/2.md
#[tokio::test]
async fn encrypted_ls() {
- let (agentfs, path, _file) = encrypted_agentfs().await;
- write_file(&agentfs.fs, "file1.txt", b"1", 0, 0)
- .await
- .unwrap();
- write_file(&agentfs.fs, "file2.txt", b"2", 0, 0)
- .await
- .unwrap();
- drop(agentfs);
+ let (vfs, path, _file) = encrypted_vfs().await;
+ write_file(&vfs.fs, "file1.txt", b"1", 0, 0).await.unwrap();
+ write_file(&vfs.fs, "file2.txt", b"2", 0, 0).await.unwrap();
+ drop(vfs);
let encryption = Some((TEST_KEY.to_string(), TEST_CIPHER.to_string()));
let mut buf = Vec::new();
@@ -622,18 +597,16 @@ f c/2.md
#[tokio::test]
async fn write_filesystem_owns_entries_as_invoking_user() {
- let (_agentfs, path, _file) = agentfs().await;
+ let (_vfs, path, _file) = vfs().await;
write_filesystem(path.clone(), "/subdir/owned.txt", "content", None)
.await
.unwrap();
- // uid/gid 0 entries make a later chmod inside `agentfs exec` (running
+ // uid/gid 0 entries make a later chmod inside `vfs exec` (running
// as the invoking uid) fail EPERM, unlike mount-created files.
// SAFETY: getuid/getgid are always safe
let (uid, gid) = unsafe { (libc::getuid(), libc::getgid()) };
- let reader = AgentFS::open(AgentFSOptions::with_path(path))
- .await
- .unwrap();
+ let reader = Vfs::open(VfsOptions::with_path(path)).await.unwrap();
let file_stats = reader.fs.stat("/subdir/owned.txt").await.unwrap().unwrap();
assert_eq!(
(file_stats.uid, file_stats.gid),
@@ -650,7 +623,7 @@ f c/2.md
#[tokio::test]
async fn encrypted_write_filesystem() {
- let (_agentfs, path, _file) = encrypted_agentfs().await;
+ let (_vfs, path, _file) = encrypted_vfs().await;
let encryption = Some((TEST_KEY.to_string(), TEST_CIPHER.to_string()));
write_filesystem(
@@ -670,7 +643,7 @@ f c/2.md
}
async fn write_file(
- fs: &agentfs_core::fs::AgentFS,
+ fs: &vfs_core::fs::Vfs,
path: &str,
data: &[u8],
uid: u32,
@@ -681,7 +654,7 @@ f c/2.md
}
let (_, file) = fs.create_file(path, S_IFREG | 0o644, uid, gid).await?;
file.pwrite(0, data).await?;
- // Tier Four: cat_filesystem opens a fresh AgentFS at the same path.
+ // Tier Four: cat_filesystem opens a fresh Vfs at the same path.
// That second instance only sees what's durable in SQLite, so the
// writer must flush its batcher before another opener can read.
fs.drain_all().await?;
diff --git a/crates/agentfs-cli/src/cmd/init.rs b/crates/vfs-cli/src/cmd/init.rs
similarity index 88%
rename from crates/agentfs-cli/src/cmd/init.rs
rename to crates/vfs-cli/src/cmd/init.rs
index ddd2384d..d4aa8dcb 100644
--- a/crates/agentfs-cli/src/cmd/init.rs
+++ b/crates/vfs-cli/src/cmd/init.rs
@@ -1,11 +1,11 @@
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
-use agentfs_core::{
- agentfs_dir, AgentFS, AgentFSOptions, EncryptionConfig, OverlayFS, PartialBootstrapStrategy,
- PartialSyncOpts, SyncOptions,
-};
use anyhow::{Context, Result as AnyhowResult};
+use vfs_core::{
+ vfs_dir, EncryptionConfig, OverlayFS, PartialBootstrapStrategy, PartialSyncOpts, SyncOptions,
+ Vfs, VfsOptions,
+};
use crate::opts::{MountBackend, SyncCommandOptions};
@@ -16,7 +16,7 @@ pub struct EncryptionOptions {
pub cipher: String,
}
-pub(crate) async fn open_agentfs(options: AgentFSOptions) -> Result {
+pub(crate) async fn open_vfs(options: VfsOptions) -> Result {
let mut options = options;
if options.core_config.is_none() {
options = options.with_core_config(crate::config::core_config_from_env());
@@ -25,7 +25,7 @@ pub(crate) async fn open_agentfs(options: AgentFSOptions) -> Result Result,
- agent: AgentFS,
+ agent: Vfs,
) -> AnyhowResult<()> {
- use agentfs_core::{FileSystem, HostFS};
- use agentfs_mount::supervise::{exit_code_for_status, run_supervised};
- use agentfs_mount::{mount_fs, MountOpts};
use std::sync::Arc;
+ use vfs_core::{FileSystem, HostFS};
+ use vfs_mount::supervise::{exit_code_for_status, run_supervised};
+ use vfs_mount::{mount_fs, MountOpts};
let fs: Arc = if let Some(ref base_path) = base {
let canonical = base_path
@@ -241,13 +240,13 @@ async fn run_init_cmd(
};
let exec_id = uuid::Uuid::new_v4().to_string();
- let mountpoint = std::env::temp_dir().join(format!("agentfs-init-{}", exec_id));
+ let mountpoint = std::env::temp_dir().join(format!("vfs-init-{}", exec_id));
std::fs::create_dir_all(&mountpoint).context("Failed to create mount directory")?;
let mount_opts = MountOpts {
mountpoint: mountpoint.clone(),
backend: backend.into(),
- fsname: format!("agentfs:{}", id),
+ fsname: format!("vfs:{}", id),
uid: None,
gid: None,
allow_other: false,
@@ -285,7 +284,7 @@ async fn run_init_cmd(
_cmd_str: String,
_backend: MountBackend,
_base: Option,
- _agent: AgentFS,
+ _agent: Vfs,
) -> AnyhowResult<()> {
anyhow::bail!("The -c option is not supported on Windows")
}
diff --git a/crates/agentfs-cli/src/cmd/mcp_server.rs b/crates/vfs-cli/src/cmd/mcp_server.rs
similarity index 85%
rename from crates/agentfs-cli/src/cmd/mcp_server.rs
rename to crates/vfs-cli/src/cmd/mcp_server.rs
index 7f6b60f8..f59086fb 100644
--- a/crates/agentfs-cli/src/cmd/mcp_server.rs
+++ b/crates/vfs-cli/src/cmd/mcp_server.rs
@@ -1,5 +1,3 @@
-use agentfs_core::fs::{FileSystem, DEFAULT_FILE_MODE};
-use agentfs_core::{AgentFS, AgentFSOptions, Stats};
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value as JsonValue};
@@ -8,8 +6,10 @@ use std::io::{self, BufRead, Write};
use std::path::Path;
use std::sync::Arc;
use tokio::sync::Mutex;
+use vfs_core::fs::{FileSystem, DEFAULT_FILE_MODE};
+use vfs_core::{Stats, Vfs, VfsOptions};
-use crate::cmd::init::{finalize_readonly, open_agentfs};
+use crate::cmd::init::{finalize_readonly, open_vfs};
/// The complete dispatchable tool surface. `tools/list`, `tools/call`
/// dispatch, and `--tools` filter validation all key off this list; the
@@ -79,8 +79,8 @@ pub async fn handle_mcp_server_command(
id_or_path: String,
tools_filter: Option>,
) -> Result<()> {
- // Resolve and open AgentFS
- let options = AgentFSOptions::resolve(&id_or_path).context(format!(
+ // Resolve and open Vfs
+ let options = VfsOptions::resolve(&id_or_path).context(format!(
"Failed to resolve agent ID or path: {}",
id_or_path
))?;
@@ -98,17 +98,17 @@ pub async fn handle_mcp_server_command(
Ok(())
}
-/// Where request handlers get their AgentFS from.
-enum AgentFsSource {
+/// Where request handlers get their Vfs from.
+enum VfsSource {
/// Ephemeral (in-memory) databases live exactly as long as this handle.
- Held(Arc),
+ Held(Arc),
/// File-backed databases are reopened per request. The database file
/// lock is exclusive (one process at a time), so holding it across the
/// whole stdio session would block every other CLI command (including
- /// `agentfs timeline` reading the tool audit) for as long as a client
+ /// `vfs timeline` reading the tool audit) for as long as a client
/// keeps the server open. Reopening per request releases the lock while
/// the server is idle.
- PerRequest(Box),
+ PerRequest(Box),
}
/// MCP Server implementation
@@ -117,14 +117,14 @@ struct McpServer {
/// guidance on the per-request reopens (the database can be replaced
/// underneath a long-lived stdio session).
id_or_path: String,
- source: AgentFsSource,
+ source: VfsSource,
enabled_tools: Option>,
}
impl McpServer {
async fn new(
id_or_path: String,
- options: AgentFSOptions,
+ options: VfsOptions,
tools_filter: Option>,
) -> Result {
let enabled_tools = match tools_filter {
@@ -160,21 +160,21 @@ impl McpServer {
};
let source = if options.db_path()? == ":memory:" {
- let agentfs = open_agentfs(options)
+ let vfs = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
- AgentFsSource::Held(Arc::new(agentfs))
+ VfsSource::Held(Arc::new(vfs))
} else {
// Open once up front so startup fails cleanly on a missing or
// incompatible database, then release the file lock with the
// single-file family restored: even this never-writing probe
// materializes a -wal sidecar (invariant I1).
- let probe = open_agentfs(options.clone())
+ let probe = open_vfs(options.clone())
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
finalize_readonly(&probe).await;
drop(probe);
- AgentFsSource::PerRequest(Box::new(options))
+ VfsSource::PerRequest(Box::new(options))
};
Ok(Self {
@@ -184,14 +184,14 @@ impl McpServer {
})
}
- async fn agentfs(&self) -> Result> {
+ async fn vfs(&self) -> Result> {
match &self.source {
- AgentFsSource::Held(agentfs) => Ok(agentfs.clone()),
- AgentFsSource::PerRequest(options) => {
- let agentfs = open_agentfs((**options).clone()).await.map_err(|err| {
+ VfsSource::Held(vfs) => Ok(vfs.clone()),
+ VfsSource::PerRequest(options) => {
+ let vfs = open_vfs((**options).clone()).await.map_err(|err| {
super::migrate::open_error_with_guidance(err, &self.id_or_path)
})?;
- Ok(Arc::new(agentfs))
+ Ok(Arc::new(vfs))
}
}
}
@@ -210,7 +210,7 @@ impl McpServer {
// Per-request opens leave a -wal next to a file-backed database
// while serving; restore the single-file family at exit even when
// the stdio loop errored, mirroring the NFS server's
- // finalize-on-shutdown (agentfs-nfs server/tcp.rs).
+ // finalize-on-shutdown (vfs-nfs server/tcp.rs).
server.lock().await.finalize_on_shutdown().await;
result
}
@@ -220,14 +220,14 @@ impl McpServer {
/// holder of the database must not turn a clean shutdown into an error.
async fn finalize_on_shutdown(&self) {
// Ephemeral databases have no on-disk family to restore.
- let AgentFsSource::PerRequest(options) = &self.source else {
+ let VfsSource::PerRequest(options) = &self.source else {
return;
};
- match open_agentfs((**options).clone())
+ match open_vfs((**options).clone())
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &self.id_or_path))
{
- Ok(agentfs) => finalize_readonly(&agentfs).await,
+ Ok(vfs) => finalize_readonly(&vfs).await,
Err(error) => eprintln!(
"Warning: Failed to reopen the database to restore the single-file family: {error:#}"
),
@@ -329,7 +329,7 @@ impl McpServer {
"resources": {}
},
"serverInfo": {
- "name": "agentfs-mcp-server",
+ "name": "vfs-mcp-server",
"version": env!("CARGO_PKG_VERSION")
}
}))
@@ -348,14 +348,14 @@ impl McpServer {
let arguments = params.get("arguments").cloned().unwrap_or(json!({}));
- let agentfs = self
- .agentfs()
+ let vfs = self
+ .vfs()
.await
.map_err(|err| RpcError::internal(format!("Failed to open database: {err:#}")))?;
// Every tools/call attempt is audited, including rejected ones, so
- // `agentfs timeline` shows the full MCP activity for the database.
- let audit_id = agentfs
+ // `vfs timeline` shows the full MCP activity for the database.
+ let audit_id = vfs
.tools
.start(name, Some(arguments.clone()))
.await
@@ -363,16 +363,15 @@ impl McpServer {
RpcError::internal(format!("Failed to record tool call audit row: {err}"))
})?;
- let outcome = self.dispatch_tool(&agentfs, name, arguments).await;
+ let outcome = self.dispatch_tool(&vfs, name, arguments).await;
match &outcome {
Ok(text) => {
- agentfs
- .tools
+ vfs.tools
.success(audit_id, Some(JsonValue::String(text.clone())))
.await
}
- Err(err) => agentfs.tools.error(audit_id, &err.message).await,
+ Err(err) => vfs.tools.error(audit_id, &err.message).await,
}
.map_err(|err| {
RpcError::internal(format!("Failed to record tool call audit row: {err}"))
@@ -392,7 +391,7 @@ impl McpServer {
async fn dispatch_tool(
&self,
- agentfs: &AgentFS,
+ vfs: &Vfs,
name: &str,
arguments: JsonValue,
) -> Result {
@@ -406,18 +405,18 @@ impl McpServer {
}
let result = match name {
- "read_file" => handle_read_file(agentfs, parse_tool_params(arguments)?).await,
- "write_file" => handle_write_file(agentfs, parse_tool_params(arguments)?).await,
- "readdir" => handle_readdir(agentfs, parse_tool_params(arguments)?).await,
- "mkdir" => handle_mkdir(agentfs, parse_tool_params(arguments)?).await,
- "rename" => handle_rename(agentfs, parse_tool_params(arguments)?).await,
- "remove" => handle_remove(agentfs, parse_tool_params(arguments)?).await,
- "stat" => handle_stat(agentfs, parse_tool_params(arguments)?).await,
- "access" => handle_access(agentfs, parse_tool_params(arguments)?).await,
- "kv_get" => handle_kv_get(agentfs, parse_tool_params(arguments)?).await,
- "kv_set" => handle_kv_set(agentfs, parse_tool_params(arguments)?).await,
- "kv_delete" => handle_kv_delete(agentfs, parse_tool_params(arguments)?).await,
- "kv_list" => handle_kv_list(agentfs, parse_tool_params(arguments)?).await,
+ "read_file" => handle_read_file(vfs, parse_tool_params(arguments)?).await,
+ "write_file" => handle_write_file(vfs, parse_tool_params(arguments)?).await,
+ "readdir" => handle_readdir(vfs, parse_tool_params(arguments)?).await,
+ "mkdir" => handle_mkdir(vfs, parse_tool_params(arguments)?).await,
+ "rename" => handle_rename(vfs, parse_tool_params(arguments)?).await,
+ "remove" => handle_remove(vfs, parse_tool_params(arguments)?).await,
+ "stat" => handle_stat(vfs, parse_tool_params(arguments)?).await,
+ "access" => handle_access(vfs, parse_tool_params(arguments)?).await,
+ "kv_get" => handle_kv_get(vfs, parse_tool_params(arguments)?).await,
+ "kv_set" => handle_kv_set(vfs, parse_tool_params(arguments)?).await,
+ "kv_delete" => handle_kv_delete(vfs, parse_tool_params(arguments)?).await,
+ "kv_list" => handle_kv_list(vfs, parse_tool_params(arguments)?).await,
_ => Err(anyhow::anyhow!(
"Tool {name} is advertised but has no dispatch arm; \
ALL_TOOLS and dispatch_tool are out of sync"
@@ -428,8 +427,8 @@ impl McpServer {
}
async fn handle_resources_list(&self) -> Result {
- let agentfs = self.agentfs().await?;
- let resources = list_resources(&agentfs).await?;
+ let vfs = self.vfs().await?;
+ let resources = list_resources(&vfs).await?;
Ok(json!({ "resources": resources }))
}
@@ -439,8 +438,8 @@ impl McpServer {
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing resource uri"))?;
- let agentfs = self.agentfs().await?;
- let contents = read_resource(&agentfs, uri).await?;
+ let vfs = self.vfs().await?;
+ let contents = read_resource(&vfs, uri).await?;
let mime_type = guess_mime_type(uri);
// Try to decode as UTF-8, fall back to base64
@@ -839,10 +838,10 @@ struct KvListParams {
// ============================================================================
/// Read file contents
-async fn handle_read_file(agentfs: &AgentFS, params: ReadFileParams) -> Result {
+async fn handle_read_file(vfs: &Vfs, params: ReadFileParams) -> Result {
let path = normalize_path(¶ms.path)?;
- let data = agentfs
+ let data = vfs
.fs
.read_file(&path)
.await
@@ -862,7 +861,7 @@ async fn handle_read_file(agentfs: &AgentFS, params: ReadFileParams) -> Result Result {
+async fn handle_write_file(vfs: &Vfs, params: WriteFileParams) -> Result {
let path = normalize_path(¶ms.path)?;
let data = match params.encoding.as_deref() {
@@ -872,12 +871,12 @@ async fn handle_write_file(agentfs: &AgentFS, params: WriteFileParams) -> Result
// Create parent directories if requested
if params.create_dirs.unwrap_or(false) {
- ensure_parent_dirs(agentfs, &path).await?;
+ ensure_parent_dirs(vfs, &path).await?;
}
- let file = match agentfs.fs.stat(&path).await? {
+ let file = match vfs.fs.stat(&path).await? {
Some(stats) if stats.is_file() => {
- let file = FileSystem::open(&agentfs.fs, stats.ino, libc::O_WRONLY)
+ let file = FileSystem::open(&vfs.fs, stats.ino, libc::O_WRONLY)
.await
.context("Failed to open file")?;
file.truncate(0).await.context("Failed to truncate file")?;
@@ -885,7 +884,7 @@ async fn handle_write_file(agentfs: &AgentFS, params: WriteFileParams) -> Result
}
Some(_) => anyhow::bail!("Not a regular file: {}", path),
None => {
- let (_, file) = agentfs
+ let (_, file) = vfs
.fs
.create_file(&path, DEFAULT_FILE_MODE, 0, 0)
.await
@@ -905,7 +904,7 @@ async fn handle_write_file(agentfs: &AgentFS, params: WriteFileParams) -> Result
/// Helper to create parent directories recursively
fn ensure_parent_dirs<'a>(
- agentfs: &'a AgentFS,
+ vfs: &'a Vfs,
path: &'a str,
) -> std::pin::Pin> + Send + 'a>> {
Box::pin(async move {
@@ -919,16 +918,15 @@ fn ensure_parent_dirs<'a>(
let parent_path = normalize_path(&parent_str)?;
// Check if parent exists
- if agentfs.fs.stat(&parent_path).await?.is_some() {
+ if vfs.fs.stat(&parent_path).await?.is_some() {
return Ok(());
}
// Recursively ensure grandparent exists
- ensure_parent_dirs(agentfs, &parent_path).await?;
+ ensure_parent_dirs(vfs, &parent_path).await?;
// Create parent
- agentfs
- .fs
+ vfs.fs
.mkdir(&parent_path, 0, 0)
.await
.context(format!("Failed to create directory: {}", parent_path))?;
@@ -938,17 +936,17 @@ fn ensure_parent_dirs<'a>(
}
/// List directory contents
-async fn handle_readdir(agentfs: &AgentFS, params: ReaddirParams) -> Result {
+async fn handle_readdir(vfs: &Vfs, params: ReaddirParams) -> Result {
let path = normalize_path(¶ms.path)?;
- let stats = agentfs
+ let stats = vfs
.fs
.stat(&path)
.await
.context("Failed to stat directory")?
.ok_or_else(|| anyhow::anyhow!("Directory not found: {}", path))?;
- let entries = agentfs
+ let entries = vfs
.fs
.readdir(stats.ino)
.await
@@ -959,11 +957,10 @@ async fn handle_readdir(agentfs: &AgentFS, params: ReaddirParams) -> Result Result {
+async fn handle_mkdir(vfs: &Vfs, params: MkdirParams) -> Result {
let path = normalize_path(¶ms.path)?;
- agentfs
- .fs
+ vfs.fs
.mkdir(&path, 0, 0)
.await
.context("Failed to create directory")?;
@@ -972,11 +969,10 @@ async fn handle_mkdir(agentfs: &AgentFS, params: MkdirParams) -> Result
}
/// Remove empty directory
-async fn handle_remove(agentfs: &AgentFS, params: RemoveParams) -> Result {
+async fn handle_remove(vfs: &Vfs, params: RemoveParams) -> Result {
let path = normalize_path(¶ms.path)?;
- agentfs
- .fs
+ vfs.fs
.remove(&path)
.await
.context("Failed to remove directory")?;
@@ -985,13 +981,13 @@ async fn handle_remove(agentfs: &AgentFS, params: RemoveParams) -> Result Result {
+async fn handle_rename(vfs: &Vfs, params: RenameParams) -> Result {
let from = normalize_path(¶ms.from)?;
let to = normalize_path(¶ms.to)?;
- let (from_parent, from_name) = agentfs.fs.resolve_parent_and_name(&from).await?;
- let (to_parent, to_name) = agentfs.fs.resolve_parent_and_name(&to).await?;
+ let (from_parent, from_name) = vfs.fs.resolve_parent_and_name(&from).await?;
+ let (to_parent, to_name) = vfs.fs.resolve_parent_and_name(&to).await?;
- FileSystem::rename(&agentfs.fs, from_parent, &from_name, to_parent, &to_name)
+ FileSystem::rename(&vfs.fs, from_parent, &from_name, to_parent, &to_name)
.await
.context("Failed to rename")?;
@@ -999,10 +995,10 @@ async fn handle_rename(agentfs: &AgentFS, params: RenameParams) -> Result Result {
+async fn handle_stat(vfs: &Vfs, params: StatParams) -> Result {
let path = normalize_path(¶ms.path)?;
- let stats = agentfs
+ let stats = vfs
.fs
.stat(&path)
.await
@@ -1013,17 +1009,17 @@ async fn handle_stat(agentfs: &AgentFS, params: StatParams) -> Result {
}
/// Test if path exists
-async fn handle_access(agentfs: &AgentFS, params: AccessParams) -> Result {
+async fn handle_access(vfs: &Vfs, params: AccessParams) -> Result {
let path = normalize_path(¶ms.path)?;
- let exists = agentfs.fs.stat(&path).await?.is_some();
+ let exists = vfs.fs.stat(&path).await?.is_some();
Ok(serde_json::to_string(&json!({ "exists": exists }))?)
}
/// Get KV value
-async fn handle_kv_get(agentfs: &AgentFS, params: KvGetParams) -> Result {
- let value: Option = agentfs
+async fn handle_kv_get(vfs: &Vfs, params: KvGetParams) -> Result {
+ let value: Option = vfs
.kv
.get(¶ms.key)
.await
@@ -1033,9 +1029,8 @@ async fn handle_kv_get(agentfs: &AgentFS, params: KvGetParams) -> Result
}
/// Set KV value
-async fn handle_kv_set(agentfs: &AgentFS, params: KvSetParams) -> Result {
- agentfs
- .kv
+async fn handle_kv_set(vfs: &Vfs, params: KvSetParams) -> Result {
+ vfs.kv
.set(¶ms.key, ¶ms.value)
.await
.context("Failed to set value")?;
@@ -1044,9 +1039,8 @@ async fn handle_kv_set(agentfs: &AgentFS, params: KvSetParams) -> Result
}
/// Delete KV value
-async fn handle_kv_delete(agentfs: &AgentFS, params: KvDeleteParams) -> Result {
- agentfs
- .kv
+async fn handle_kv_delete(vfs: &Vfs, params: KvDeleteParams) -> Result {
+ vfs.kv
.delete(¶ms.key)
.await
.context("Failed to delete key")?;
@@ -1055,8 +1049,8 @@ async fn handle_kv_delete(agentfs: &AgentFS, params: KvDeleteParams) -> Result Result {
- let mut keys = agentfs.kv.keys().await.context("Failed to list keys")?;
+async fn handle_kv_list(vfs: &Vfs, params: KvListParams) -> Result {
+ let mut keys = vfs.kv.keys().await.context("Failed to list keys")?;
if let Some(prefix) = ¶ms.prefix {
keys.retain(|key| key.starts_with(prefix.as_str()));
}
@@ -1066,25 +1060,25 @@ async fn handle_kv_list(agentfs: &AgentFS, params: KvListParams) -> Result Result> {
+async fn list_resources(vfs: &Vfs) -> Result> {
let mut resources = Vec::new();
- collect_file_resources(agentfs, "/", &mut resources).await?;
+ collect_file_resources(vfs, "/", &mut resources).await?;
Ok(resources)
}
/// Recursively collect file resources
fn collect_file_resources<'a>(
- agentfs: &'a AgentFS,
+ vfs: &'a Vfs,
path: &'a str,
resources: &'a mut Vec,
) -> std::pin::Pin> + Send + 'a>> {
Box::pin(async move {
- let dir_stats = match agentfs.fs.stat(path).await? {
+ let dir_stats = match vfs.fs.stat(path).await? {
Some(s) => s,
None => return Ok(()),
};
- let entries = match agentfs.fs.readdir(dir_stats.ino).await? {
+ let entries = match vfs.fs.readdir(dir_stats.ino).await? {
Some(entries) => entries,
None => return Ok(()),
};
@@ -1096,7 +1090,7 @@ fn collect_file_resources<'a>(
format!("{}/{}", path, entry)
};
- let stats = match agentfs.fs.stat(&full_path).await? {
+ let stats = match vfs.fs.stat(&full_path).await? {
Some(s) => s,
None => continue,
};
@@ -1110,7 +1104,7 @@ fn collect_file_resources<'a>(
}));
} else if stats.is_directory() {
// Recurse into subdirectory
- collect_file_resources(agentfs, &full_path, resources).await?;
+ collect_file_resources(vfs, &full_path, resources).await?;
}
}
@@ -1119,10 +1113,10 @@ fn collect_file_resources<'a>(
}
/// Read a resource by path
-async fn read_resource(agentfs: &AgentFS, path: &str) -> Result> {
+async fn read_resource(vfs: &Vfs, path: &str) -> Result> {
let normalized = normalize_path(path)?;
- let data = agentfs
+ let data = vfs
.fs
.read_file(&normalized)
.await
diff --git a/crates/agentfs-cli/src/cmd/mcp_server/tests.rs b/crates/vfs-cli/src/cmd/mcp_server/tests.rs
similarity index 82%
rename from crates/agentfs-cli/src/cmd/mcp_server/tests.rs
rename to crates/vfs-cli/src/cmd/mcp_server/tests.rs
index 1c1974bb..90fe2f68 100644
--- a/crates/agentfs-cli/src/cmd/mcp_server/tests.rs
+++ b/crates/vfs-cli/src/cmd/mcp_server/tests.rs
@@ -1,6 +1,6 @@
use super::*;
-use agentfs_core::fs::S_IFREG;
-use agentfs_core::{error::Error as SdkError, FsError, ToolCallStatus};
+use vfs_core::fs::S_IFREG;
+use vfs_core::{error::Error as SdkError, FsError, ToolCallStatus};
const ROOT_INO: i64 = 1;
@@ -8,21 +8,21 @@ const ROOT_INO: i64 = 1;
/// audit rows are shared across requests within one server. The PerRequest
/// reopen semantics are inherently cross-process and are covered by the
/// stdio shell suite (tests/test-mcp-server.sh).
-async fn create_test_server() -> Result<(McpServer, Arc)> {
+async fn create_test_server() -> Result<(McpServer, Arc)> {
create_filtered_test_server(None).await
}
async fn create_filtered_test_server(
tools_filter: Option>,
-) -> Result<(McpServer, Arc)> {
+) -> Result<(McpServer, Arc)> {
let server = McpServer::new(
":memory:".to_string(),
- AgentFSOptions::ephemeral(),
+ VfsOptions::ephemeral(),
tools_filter,
)
.await?;
- let agentfs = server.agentfs().await?;
- Ok((server, agentfs))
+ let vfs = server.vfs().await?;
+ Ok((server, vfs))
}
async fn call_tool(server: &McpServer, id: i64, name: &str, arguments: JsonValue) -> JsonValue {
@@ -52,7 +52,7 @@ fn advertised_tool_names(server: &McpServer) -> Vec {
#[tokio::test]
async fn tools_list_equals_dispatch_surface() -> Result<()> {
- let (server, agentfs) = create_test_server().await?;
+ let (server, vfs) = create_test_server().await?;
assert_eq!(
advertised_tool_names(&server),
@@ -61,7 +61,7 @@ async fn tools_list_equals_dispatch_surface() -> Result<()> {
);
// Every advertised tool must dispatch with minimal valid arguments.
- agentfs.fs.mkdir("/dir", 0, 0).await?;
+ vfs.fs.mkdir("/dir", 0, 0).await?;
let minimal_args = |name: &str| -> JsonValue {
match name {
"read_file" => json!({"path": "/seed.txt"}),
@@ -120,17 +120,17 @@ async fn tools_list_equals_dispatch_surface() -> Result<()> {
#[tokio::test]
async fn kv_list_dispatches_and_honors_prefix() -> Result<()> {
- let (_server, agentfs) = create_test_server().await?;
- agentfs.kv.set("app/one", &json!(1)).await?;
- agentfs.kv.set("app/two", &json!(2)).await?;
- agentfs.kv.set("other", &json!(3)).await?;
+ let (_server, vfs) = create_test_server().await?;
+ vfs.kv.set("app/one", &json!(1)).await?;
+ vfs.kv.set("app/two", &json!(2)).await?;
+ vfs.kv.set("other", &json!(3)).await?;
- let all = handle_kv_list(&agentfs, KvListParams { prefix: None }).await?;
+ let all = handle_kv_list(&vfs, KvListParams { prefix: None }).await?;
let all: Vec = serde_json::from_str(&all)?;
assert_eq!(all, vec!["app/one", "app/two", "other"]);
let filtered = handle_kv_list(
- &agentfs,
+ &vfs,
KvListParams {
prefix: Some("app/".to_string()),
},
@@ -143,7 +143,7 @@ async fn kv_list_dispatches_and_honors_prefix() -> Result<()> {
#[tokio::test]
async fn tool_calls_are_audited_with_status_and_timing() -> Result<()> {
- let (server, agentfs) = create_test_server().await?;
+ let (server, vfs) = create_test_server().await?;
let ok = call_tool(
&server,
@@ -160,7 +160,7 @@ async fn tool_calls_are_audited_with_status_and_timing() -> Result<()> {
"read_file on a missing path must fail"
);
- let calls = agentfs.tools.recent(None).await?;
+ let calls = vfs.tools.recent(None).await?;
assert_eq!(calls.len(), 2, "each tools/call must write one audit row");
// recent() orders by started_at, which can tie at millisecond
@@ -202,9 +202,9 @@ async fn tool_calls_are_audited_with_status_and_timing() -> Result<()> {
#[tokio::test]
async fn write_file_preserves_existing_mode_and_creates_with_default() -> Result<()> {
- let (server, agentfs) = create_test_server().await?;
+ let (server, vfs) = create_test_server().await?;
- let (created, file) = agentfs
+ let (created, file) = vfs
.fs
.create_file("/mode.txt", S_IFREG | 0o755, 0, 0)
.await?;
@@ -221,7 +221,7 @@ async fn write_file_preserves_existing_mode_and_creates_with_default() -> Result
.await;
assert!(response.get("error").is_none(), "write failed: {response}");
- let stats = agentfs.fs.stat("/mode.txt").await?.unwrap();
+ let stats = vfs.fs.stat("/mode.txt").await?.unwrap();
assert_eq!(
stats.mode,
S_IFREG | 0o755,
@@ -229,7 +229,7 @@ async fn write_file_preserves_existing_mode_and_creates_with_default() -> Result
);
assert_eq!(stats.size, "overwritten".len() as i64);
assert_eq!(
- agentfs.fs.read_file("/mode.txt").await?.unwrap(),
+ vfs.fs.read_file("/mode.txt").await?.unwrap(),
b"overwritten".to_vec()
);
@@ -241,7 +241,7 @@ async fn write_file_preserves_existing_mode_and_creates_with_default() -> Result
)
.await;
assert!(response.get("error").is_none(), "write failed: {response}");
- let stats = agentfs.fs.stat("/fresh.txt").await?.unwrap();
+ let stats = vfs.fs.stat("/fresh.txt").await?.unwrap();
assert_eq!(
stats.mode, DEFAULT_FILE_MODE,
"new files must be created with the documented default mode 0644"
@@ -251,7 +251,7 @@ async fn write_file_preserves_existing_mode_and_creates_with_default() -> Result
#[tokio::test]
async fn unknown_tool_is_rejected_with_invalid_params_and_audited() -> Result<()> {
- let (server, agentfs) = create_test_server().await?;
+ let (server, vfs) = create_test_server().await?;
let response = call_tool(&server, 1, "copy_file", json!({})).await;
let error = response
@@ -270,7 +270,7 @@ async fn unknown_tool_is_rejected_with_invalid_params_and_audited() -> Result<()
"error should name the unknown tool: {error}"
);
- let calls = agentfs.tools.recent(None).await?;
+ let calls = vfs.tools.recent(None).await?;
assert_eq!(calls.len(), 1, "rejected calls are audited too");
assert_eq!(calls[0].name, "copy_file");
assert_eq!(calls[0].status, ToolCallStatus::Error);
@@ -279,7 +279,7 @@ async fn unknown_tool_is_rejected_with_invalid_params_and_audited() -> Result<()
#[tokio::test]
async fn notifications_are_accepted_without_response() -> Result<()> {
- let (server, _agentfs) = create_test_server().await?;
+ let (server, _vfs) = create_test_server().await?;
// The standard post-initialize notification from strict clients.
assert_eq!(
@@ -312,7 +312,7 @@ async fn notifications_are_accepted_without_response() -> Result<()> {
#[tokio::test]
async fn unknown_method_with_id_returns_method_not_found() -> Result<()> {
- let (server, _agentfs) = create_test_server().await?;
+ let (server, _vfs) = create_test_server().await?;
let response = server
.handle_request(json!({"jsonrpc": "2.0", "id": 7, "method": "bogus/method"}))
@@ -329,7 +329,7 @@ async fn unknown_method_with_id_returns_method_not_found() -> Result<()> {
#[tokio::test]
async fn tools_filter_limits_both_listing_and_dispatch() -> Result<()> {
- let (server, agentfs) =
+ let (server, vfs) =
create_filtered_test_server(Some(vec!["read_file".to_string(), "kv_list".to_string()]))
.await?;
@@ -354,11 +354,11 @@ async fn tools_filter_limits_both_listing_and_dispatch() -> Result<()> {
Some(INVALID_PARAMS)
);
assert!(
- agentfs.fs.stat("/blocked.txt").await?.is_none(),
+ vfs.fs.stat("/blocked.txt").await?.is_none(),
"disabled tool must not have side effects"
);
- let calls = agentfs.tools.recent(None).await?;
+ let calls = vfs.tools.recent(None).await?;
assert_eq!(calls.len(), 1);
assert_eq!(calls[0].status, ToolCallStatus::Error);
Ok(())
@@ -368,7 +368,7 @@ async fn tools_filter_limits_both_listing_and_dispatch() -> Result<()> {
async fn unknown_tools_filter_is_rejected_at_startup() -> Result<()> {
let error = match McpServer::new(
":memory:".to_string(),
- AgentFSOptions::ephemeral(),
+ VfsOptions::ephemeral(),
Some(vec!["copy_file".to_string()]),
)
.await
@@ -387,18 +387,18 @@ async fn unknown_tools_filter_is_rejected_at_startup() -> Result<()> {
#[tokio::test]
async fn mcp_rename_directory_into_own_subtree_returns_error_and_preserves_namespace() -> Result<()>
{
- let (server, agentfs) = create_test_server().await?;
- agentfs.fs.mkdir("/parent", 0, 0).await?;
- agentfs.fs.mkdir("/parent/child", 0, 0).await?;
+ let (server, vfs) = create_test_server().await?;
+ vfs.fs.mkdir("/parent", 0, 0).await?;
+ vfs.fs.mkdir("/parent/child", 0, 0).await?;
- let parent_ino = agentfs.fs.stat("/parent").await?.unwrap().ino;
- let child_ino = agentfs.fs.stat("/parent/child").await?.unwrap().ino;
- let root_before = agentfs.fs.readdir(ROOT_INO).await?.unwrap();
- let parent_before = agentfs.fs.readdir(parent_ino).await?.unwrap();
- let child_before = agentfs.fs.readdir(child_ino).await?.unwrap();
+ let parent_ino = vfs.fs.stat("/parent").await?.unwrap().ino;
+ let child_ino = vfs.fs.stat("/parent/child").await?.unwrap().ino;
+ let root_before = vfs.fs.readdir(ROOT_INO).await?.unwrap();
+ let parent_before = vfs.fs.readdir(parent_ino).await?.unwrap();
+ let child_before = vfs.fs.readdir(child_ino).await?.unwrap();
let direct_error = handle_rename(
- &agentfs,
+ &vfs,
RenameParams {
from: "/parent".to_string(),
to: "/parent/child/parent".to_string(),
@@ -436,14 +436,11 @@ async fn mcp_rename_directory_into_own_subtree_returns_error_and_preserves_names
"cycle rename should return a JSON-RPC error response: {response}"
);
- assert_eq!(agentfs.fs.readdir(ROOT_INO).await?.unwrap(), root_before);
- assert_eq!(
- agentfs.fs.readdir(parent_ino).await?.unwrap(),
- parent_before
- );
- assert_eq!(agentfs.fs.readdir(child_ino).await?.unwrap(), child_before);
- assert!(agentfs.fs.stat("/parent").await?.is_some());
- assert!(agentfs.fs.stat("/parent/child").await?.is_some());
- assert!(agentfs.fs.stat("/parent/child/parent").await?.is_none());
+ assert_eq!(vfs.fs.readdir(ROOT_INO).await?.unwrap(), root_before);
+ assert_eq!(vfs.fs.readdir(parent_ino).await?.unwrap(), parent_before);
+ assert_eq!(vfs.fs.readdir(child_ino).await?.unwrap(), child_before);
+ assert!(vfs.fs.stat("/parent").await?.is_some());
+ assert!(vfs.fs.stat("/parent/child").await?.is_some());
+ assert!(vfs.fs.stat("/parent/child/parent").await?.is_none());
Ok(())
}
diff --git a/crates/agentfs-cli/src/cmd/migrate.rs b/crates/vfs-cli/src/cmd/migrate.rs
similarity index 98%
rename from crates/agentfs-cli/src/cmd/migrate.rs
rename to crates/vfs-cli/src/cmd/migrate.rs
index 876d0b29..9b02ae16 100644
--- a/crates/agentfs-cli/src/cmd/migrate.rs
+++ b/crates/vfs-cli/src/cmd/migrate.rs
@@ -1,16 +1,11 @@
//! Database schema migration command.
//!
-//! One `agentfs migrate` lands any supported old schema at the current
+//! One `vfs migrate` lands any supported old schema at the current
//! version: in place by default (every supported migration is an additive,
//! transactional ALTER), or copy-based via `--copy ` for users who
//! want a rebuilt database with the current chunk layout, keeping the
//! hash/verify engine from the historical `migrate-v0-5` command.
-use agentfs_core::{
- config::{DEFAULT_CHUNK_SIZE, DEFAULT_INLINE_THRESHOLD},
- error::Error as SdkError,
- schema, AgentFSOptions, SchemaVersion,
-};
use anyhow::{Context, Result as AnyhowResult};
use std::collections::{hash_map::DefaultHasher, HashSet};
use std::fs;
@@ -19,6 +14,11 @@ use std::io::{Read as IoRead, Write};
use std::path::{Path, PathBuf};
use turso::transaction::{Transaction, TransactionBehavior};
use turso::{Connection, Value};
+use vfs_core::{
+ config::{DEFAULT_CHUNK_SIZE, DEFAULT_INLINE_THRESHOLD},
+ error::Error as SdkError,
+ schema, SchemaVersion, VfsOptions,
+};
use super::safety::{build_local_database, ReadOnlyOpenSidecars};
@@ -28,19 +28,19 @@ const S_IFREG: i64 = 0o100000;
/// Guidance for a schema-version mismatch surfaced by an open path.
///
/// Names a command that actually finishes the job: supported old versions get
-/// `agentfs migrate ` (which lands at CURRENT in one invocation);
-/// anything else is from a newer agentfs and no local command can help.
+/// `vfs migrate ` (which lands at CURRENT in one invocation);
+/// anything else is from a newer vfs and no local command can help.
fn schema_upgrade_guidance(found: &str, expected: &str, id_or_path: &str) -> String {
match SchemaVersion::parse(found) {
Some(version) if version < schema::CURRENT => format!(
"Filesystem `{id_or_path}` requires migration\n\n\
- Found schema version {found}, but this version of agentfs requires {expected}.\n\n\
- To upgrade, run:\n\n agentfs migrate {id_or_path}\n"
+ Found schema version {found}, but this version of vfs requires {expected}.\n\n\
+ To upgrade, run:\n\n vfs migrate {id_or_path}\n"
),
_ => format!(
"Filesystem `{id_or_path}` has unsupported schema version {found}; \
- this version of agentfs supports up to {expected}.\n\
- The database was likely created by a newer agentfs. Upgrade agentfs to open it."
+ this version of vfs supports up to {expected}.\n\
+ The database was likely created by a newer vfs. Upgrade vfs to open it."
),
}
}
@@ -63,7 +63,7 @@ pub async fn handle_migrate_command(
dry_run: bool,
encryption: Option<&(String, String)>,
) -> AnyhowResult<()> {
- let options = AgentFSOptions::resolve(&id_or_path)?;
+ let options = VfsOptions::resolve(&id_or_path)?;
let db_path_str = options
.db_path()
.context("Failed to resolve database path")?;
@@ -157,7 +157,7 @@ pub async fn handle_migrate_copy_command(
overwrite_target: bool,
encryption: Option<&(String, String)>,
) -> AnyhowResult<()> {
- let options = AgentFSOptions::resolve(&id_or_path)?;
+ let options = VfsOptions::resolve(&id_or_path)?;
let source_path_str = options
.db_path()
.context("Failed to resolve database path")?;
@@ -239,8 +239,8 @@ async fn copy_migrate_to_current(
.unwrap_or(SchemaVersion::V0_0);
if source_version == schema::CURRENT {
anyhow::bail!(
- "Source database is already at schema {}. Run `agentfs migrate {}` for in-place \
- normalization or `agentfs backup` for a portable copy.",
+ "Source database is already at schema {}. Run `vfs migrate {}` for in-place \
+ normalization or `vfs backup` for a portable copy.",
schema::CURRENT,
source_path.display()
);
@@ -320,7 +320,7 @@ async fn create_current_schema(conn: &Connection) -> AnyhowResult<()> {
/// The v0.0 -> v0.2 nlink backfill rule, applied on the copy target when the
/// source predates the nlink column. Must stay in step with the in-place
-/// migration in `agentfs_core::schema`.
+/// migration in `vfs_core::schema`.
const NLINK_BACKFILL_CASE: &str = "CASE
WHEN fs_inode.ino = 1 THEN 2
WHEN (fs_inode.mode & 61440) = 16384
@@ -2028,7 +2028,7 @@ mod tests {
] {
let guidance = schema_upgrade_guidance(found, "0.5", id_or_path);
assert!(
- guidance.contains(&format!("agentfs migrate {id_or_path}")),
+ guidance.contains(&format!("vfs migrate {id_or_path}")),
"{found}: {guidance}"
);
assert!(!guidance.contains("migrate-v0-5"), "{found}: {guidance}");
@@ -2036,10 +2036,10 @@ mod tests {
let future = schema_upgrade_guidance("user_version 7", "0.5", "my-agent");
assert!(
- !future.contains("agentfs migrate"),
+ !future.contains("vfs migrate"),
"future versions must not promise migrate can fix them: {future}"
);
- assert!(future.contains("newer agentfs"), "{future}");
+ assert!(future.contains("newer vfs"), "{future}");
}
#[tokio::test]
@@ -2168,11 +2168,10 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("current.db");
{
- let agent = agentfs_core::AgentFS::open(agentfs_core::AgentFSOptions::with_path(
- db_path.to_string_lossy(),
- ))
- .await
- .unwrap();
+ let agent =
+ vfs_core::Vfs::open(vfs_core::VfsOptions::with_path(db_path.to_string_lossy()))
+ .await
+ .unwrap();
agent.fs.finalize().await.unwrap();
}
let wal = PathBuf::from(format!("{}-wal", db_path.display()));
diff --git a/crates/agentfs-cli/src/cmd/mod.rs b/crates/vfs-cli/src/cmd/mod.rs
similarity index 100%
rename from crates/agentfs-cli/src/cmd/mod.rs
rename to crates/vfs-cli/src/cmd/mod.rs
diff --git a/crates/agentfs-cli/src/cmd/mount.rs b/crates/vfs-cli/src/cmd/mount.rs
similarity index 91%
rename from crates/agentfs-cli/src/cmd/mount.rs
rename to crates/vfs-cli/src/cmd/mount.rs
index b16e1e38..c11a99b5 100644
--- a/crates/agentfs-cli/src/cmd/mount.rs
+++ b/crates/vfs-cli/src/cmd/mount.rs
@@ -1,22 +1,20 @@
-use agentfs_core::{
- AgentFSOptions, EncryptionConfig, FileSystem, HostFS, OverlayFS, PartialOriginPolicy,
-};
-use agentfs_mount::{mount_fs, Backend, MountOpts};
use anyhow::Result;
use std::{path::PathBuf, sync::Arc};
use turso::value::Value;
+use vfs_core::{EncryptionConfig, FileSystem, HostFS, OverlayFS, PartialOriginPolicy, VfsOptions};
+use vfs_mount::{mount_fs, Backend, MountOpts};
-#[cfg(target_os = "linux")]
-use agentfs_core::{error::Error as SdkError, get_mounts, Mount};
#[cfg(target_os = "linux")]
use std::{
io::{self, Write},
os::unix::fs::MetadataExt,
path::Path,
};
+#[cfg(target_os = "linux")]
+use vfs_core::{error::Error as SdkError, get_mounts, Mount};
#[cfg(target_os = "linux")]
-use crate::cmd::init::open_agentfs;
+use crate::cmd::init::open_vfs;
use crate::opts::MountBackend;
@@ -47,10 +45,7 @@ pub struct MountArgs {
pub encryption: Option<(String, String)>,
}
-fn apply_encryption(
- options: AgentFSOptions,
- encryption: &Option<(String, String)>,
-) -> AgentFSOptions {
+fn apply_encryption(options: VfsOptions, encryption: &Option<(String, String)>) -> VfsOptions {
match encryption {
Some((key, cipher)) => options.with_encryption(EncryptionConfig {
hex_key: key.clone(),
@@ -76,7 +71,7 @@ pub fn mount(args: MountArgs) -> Result<()> {
MountBackend::Fuse => {
anyhow::bail!(
"FUSE mounting is not supported on macOS.\n\
- Use --backend nfs (default) or `agentfs nfs` instead."
+ Use --backend nfs (default) or `vfs nfs` instead."
);
}
MountBackend::Nfs => mount_nfs(args),
@@ -101,7 +96,7 @@ fn mount_nfs(args: MountArgs) -> Result<()> {
rt.block_on(mount_nfs_backend(args, mountpoint))
} else {
let ready_mountpoint = mountpoint.clone();
- agentfs_mount::daemon::daemonize(
+ vfs_mount::daemon::daemonize(
move || {
// The forked daemon shares the parent's spill dir, which the
// parent removes when it exits; move off it before DB work.
@@ -109,7 +104,7 @@ fn mount_nfs(args: MountArgs) -> Result<()> {
let rt = crate::get_runtime();
rt.block_on(mount_nfs_backend(args, mountpoint))
},
- move || agentfs_mount::is_mountpoint(&ready_mountpoint),
+ move || vfs_mount::is_mountpoint(&ready_mountpoint),
std::time::Duration::from_secs(10),
)
}
@@ -118,7 +113,7 @@ fn mount_nfs(args: MountArgs) -> Result<()> {
/// Mount the agent filesystem using FUSE (Linux only).
#[cfg(target_os = "linux")]
fn mount_fuse(args: MountArgs) -> Result<()> {
- let opts = apply_encryption(AgentFSOptions::resolve(&args.id_or_path)?, &args.encryption);
+ let opts = apply_encryption(VfsOptions::resolve(&args.id_or_path)?, &args.encryption);
// Check schema version before daemonizing. This allows us to show the error
// message to the user directly, rather than having it appear in daemon logs.
@@ -138,7 +133,7 @@ fn mount_fuse(args: MountArgs) -> Result<()> {
}
let fsname = format!(
- "agentfs:{}",
+ "vfs:{}",
std::fs::canonicalize(&args.id_or_path)
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| args.id_or_path.clone())
@@ -174,15 +169,15 @@ fn mount_fuse(args: MountArgs) -> Result<()> {
let partial_origin_policy = args.partial_origin_policy;
let mount = move || {
let rt = crate::get_runtime();
- let agentfs = rt
- .block_on(open_agentfs(opts))
+ let vfs = rt
+ .block_on(open_vfs(opts))
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
// Check for overlay configuration
let fs: Arc = rt.block_on(async {
// Query base_path in a separate scope so connection is released
let base_path: Option = {
- let conn = agentfs.get_connection().await?;
+ let conn = vfs.get_connection().await?;
let query = "SELECT value FROM fs_overlay_config WHERE key = 'base_path'";
match conn.query(query, ()).await {
Ok(mut rows) => {
@@ -208,15 +203,15 @@ fn mount_fuse(args: MountArgs) -> Result<()> {
let hostfs = HostFS::new(&base_path)?;
let hostfs = hostfs.with_fuse_mountpoint(mountpoint_ino);
let overlay = if let Some(policy) = partial_origin_policy {
- OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), agentfs.fs, policy)
+ OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), vfs.fs, policy)
} else {
- OverlayFS::new(Arc::new(hostfs), agentfs.fs)
+ OverlayFS::new(Arc::new(hostfs), vfs.fs)
};
overlay.load().await?; // Load persisted whiteouts and origin mappings
Ok::, anyhow::Error>(Arc::new(overlay))
} else {
- // Plain AgentFS
- Ok(Arc::new(agentfs.fs) as Arc)
+ // Plain Vfs
+ Ok(Arc::new(vfs.fs) as Arc)
}
})?;
@@ -226,14 +221,14 @@ fn mount_fuse(args: MountArgs) -> Result<()> {
if foreground {
mount()
} else {
- agentfs_mount::daemon::daemonize(
+ vfs_mount::daemon::daemonize(
move || {
// The forked daemon shares the parent's spill dir, which the
// parent removes when it exits; move off it before DB work.
crate::config::adopt_private_spill_dir();
mount()
},
- move || agentfs_mount::is_mountpoint(&mountpoint),
+ move || vfs_mount::is_mountpoint(&mountpoint),
std::time::Duration::from_secs(10),
)
}
@@ -251,33 +246,33 @@ async fn require_schema_current_for_mount_precheck(
.await
.map_err(|err| SdkError::Internal(format!("{err:#}")))?;
let conn = db.connect()?;
- agentfs_core::schema::require_current(&conn).await
+ vfs_core::schema::require_current(&conn).await
}
/// Mount the agent filesystem using NFS over localhost.
///
/// `mountpoint` is pre-canonicalized by `mount_nfs` before any fork.
async fn mount_nfs_backend(args: MountArgs, mountpoint: PathBuf) -> Result<()> {
- use crate::cmd::init::open_agentfs;
+ use crate::cmd::init::open_vfs;
- let opts = apply_encryption(AgentFSOptions::resolve(&args.id_or_path)?, &args.encryption);
+ let opts = apply_encryption(VfsOptions::resolve(&args.id_or_path)?, &args.encryption);
let fsname = format!(
- "agentfs:{}",
+ "vfs:{}",
std::fs::canonicalize(&args.id_or_path)
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| args.id_or_path.clone())
);
- // Open AgentFS
- let agentfs = open_agentfs(opts)
+ // Open Vfs
+ let vfs = open_vfs(opts)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &args.id_or_path))?;
// Check for overlay configuration
// Query base_path in a separate scope so connection is released before load_whiteouts
let base_path: Option = {
- let conn = agentfs.get_connection().await?;
+ let conn = vfs.get_connection().await?;
let query = "SELECT value FROM fs_overlay_config WHERE key = 'base_path'";
match conn.query(query, ()).await {
Ok(mut rows) => {
@@ -302,15 +297,15 @@ async fn mount_nfs_backend(args: MountArgs, mountpoint: PathBuf) -> Result<()> {
eprintln!("Using overlay filesystem with base: {}", base_path);
let hostfs = HostFS::new(&base_path)?;
let overlay = if let Some(policy) = args.partial_origin_policy {
- OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), agentfs.fs, policy)
+ OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), vfs.fs, policy)
} else {
- OverlayFS::new(Arc::new(hostfs), agentfs.fs)
+ OverlayFS::new(Arc::new(hostfs), vfs.fs)
};
overlay.load().await?; // Load persisted whiteouts and origin mappings
Arc::new(overlay) as Arc
} else {
- // Plain AgentFS
- Arc::new(agentfs.fs) as Arc
+ // Plain Vfs
+ Arc::new(vfs.fs) as Arc
};
let mount_opts = MountOpts {
@@ -340,16 +335,16 @@ async fn run_mount_session(
eprintln!("Press Ctrl+C to unmount and exit.");
}
- agentfs_mount::supervise::supervise_mount(handle).await
+ vfs_mount::supervise::supervise_mount(handle).await
}
-/// List all currently mounted agentfs filesystems (Linux)
+/// List all currently mounted vfs filesystems (Linux)
#[cfg(target_os = "linux")]
pub fn list_mounts(out: &mut W) {
let mounts = get_mounts();
if mounts.is_empty() {
- let _ = writeln!(out, "No agentfs filesystems mounted.");
+ let _ = writeln!(out, "No vfs filesystems mounted.");
return;
}
@@ -385,7 +380,7 @@ pub fn list_mounts(out: &mut W) {
}
}
-/// List all currently mounted agentfs filesystems (macOS stub)
+/// List all currently mounted vfs filesystems (macOS stub)
#[cfg(target_os = "macos")]
pub fn list_mounts(out: &mut W) {
let _ = writeln!(out, "Mount listing is only available on Linux.");
@@ -482,9 +477,9 @@ fn confirm(prompt: &str) -> bool {
matches!(input.trim().to_lowercase().as_str(), "y" | "yes")
}
-/// Prune unused agentfs mount points.
+/// Prune unused vfs mount points.
///
-/// Finds all mounted agentfs filesystems that are not in use by any process
+/// Finds all mounted vfs filesystems that are not in use by any process
/// and unmounts them.
#[cfg(target_os = "linux")]
pub fn prune_mounts(force: bool) -> Result<()> {
@@ -545,7 +540,7 @@ pub fn prune_mounts(force: bool) -> Result<()> {
Ok(())
}
-/// Prune unused agentfs mount points (macOS stub).
+/// Prune unused vfs mount points (macOS stub).
#[cfg(target_os = "macos")]
pub fn prune_mounts(_force: bool) -> Result<()> {
anyhow::bail!("Mount pruning is only available on Linux")
@@ -602,8 +597,8 @@ mod tests {
let dir = tempdir().unwrap();
let db_path = dir.path().join("encrypted.db");
- let agentfs = agentfs_core::AgentFS::open(
- AgentFSOptions::with_path(db_path.to_str().unwrap().to_string()).with_encryption(
+ let vfs = vfs_core::Vfs::open(
+ VfsOptions::with_path(db_path.to_str().unwrap().to_string()).with_encryption(
EncryptionConfig {
hex_key: TEST_KEY.to_string(),
cipher: TEST_CIPHER.to_string(),
@@ -612,7 +607,7 @@ mod tests {
)
.await
.unwrap();
- drop(agentfs);
+ drop(vfs);
let encryption = (TEST_KEY.to_string(), TEST_CIPHER.to_string());
require_schema_current_for_mount_precheck(db_path.to_str().unwrap(), Some(&encryption))
diff --git a/crates/agentfs-cli/src/cmd/nfs.rs b/crates/vfs-cli/src/cmd/nfs.rs
similarity index 75%
rename from crates/agentfs-cli/src/cmd/nfs.rs
rename to crates/vfs-cli/src/cmd/nfs.rs
index cfa7421e..239315f4 100644
--- a/crates/agentfs-cli/src/cmd/nfs.rs
+++ b/crates/vfs-cli/src/cmd/nfs.rs
@@ -1,17 +1,17 @@
//! Standalone NFS server command.
//!
-//! This module provides a standalone NFS server that exports an AgentFS
+//! This module provides a standalone NFS server that exports a Vfs
//! filesystem over the network, allowing remote systems (like VMs) to mount
//! it as their root filesystem.
-use agentfs_core::{agentfs_dir, AgentFSOptions, EncryptionConfig, FileSystem, HostFS, OverlayFS};
-use agentfs_mount::{serve_nfs, NfsServerOptions};
use anyhow::{Context, Result};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::signal;
+use vfs_core::{vfs_dir, EncryptionConfig, FileSystem, HostFS, OverlayFS, VfsOptions};
+use vfs_mount::{serve_nfs, NfsServerOptions};
-use crate::cmd::init::open_agentfs;
+use crate::cmd::init::open_vfs;
/// Handle the `nfs` command - start a standalone NFS server.
pub async fn handle_nfs_command(
@@ -23,39 +23,39 @@ pub async fn handle_nfs_command(
// Resolve database path
let db_path = resolve_db_path(&id_or_path)?;
- // Open or create the AgentFS database
+ // Open or create the Vfs database
let db_path_str = db_path
.to_str()
.context("Database path contains non-UTF8 characters")?;
- let mut options = AgentFSOptions::with_path(db_path_str);
+ let mut options = VfsOptions::with_path(db_path_str);
if let Some((key, cipher)) = encryption {
options = options.with_encryption(EncryptionConfig {
hex_key: key,
cipher,
});
}
- let agentfs = open_agentfs(options)
+ let vfs = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
// Check if overlay is configured in the database
- let base_path = agentfs
+ let base_path = vfs
.is_overlay_enabled()
.await
.context("Failed to check overlay config")?;
- // Create filesystem - either direct AgentFS or overlay with base
+ // Create filesystem - either direct Vfs or overlay with base
let fs: Arc = if let Some(base_str) = base_path {
let hostfs = HostFS::new(&base_str).context("Failed to create HostFS")?;
- let overlay = OverlayFS::new(Arc::new(hostfs), agentfs.fs);
+ let overlay = OverlayFS::new(Arc::new(hostfs), vfs.fs);
overlay.load().await?; // Load persisted whiteouts and origin mappings
eprintln!("Mode: overlay (base: {})", base_str);
Arc::new(overlay)
} else {
- eprintln!("Mode: direct AgentFS");
- Arc::new(agentfs.fs)
+ eprintln!("Mode: direct Vfs");
+ Arc::new(vfs.fs)
};
// Bind NFS server
@@ -67,7 +67,7 @@ pub async fn handle_nfs_command(
// Print server info
eprintln!();
- eprintln!("AgentFS NFS Server");
+ eprintln!("Vfs NFS Server");
eprintln!(" Database: {}", db_path.display());
eprintln!(" Listening: {}", listen_addr);
eprintln!(" Export: /");
@@ -107,14 +107,14 @@ fn resolve_db_path(id_or_path: &str) -> Result {
return Ok(path);
}
- // Otherwise, treat it as an agent ID and look in .agentfs/
- let agentfs_dir = agentfs_dir();
- let db_path = agentfs_dir.join(format!("{}.db", id_or_path));
+ // Otherwise, treat it as an agent ID and look in .vfs/
+ let vfs_dir = vfs_dir();
+ let db_path = vfs_dir.join(format!("{}.db", id_or_path));
if db_path.exists() {
Ok(db_path)
} else {
- // If it doesn't exist, still return the path - AgentFS will create it
+ // If it doesn't exist, still return the path - Vfs will create it
Ok(db_path)
}
}
diff --git a/crates/agentfs-cli/src/cmd/profiling.rs b/crates/vfs-cli/src/cmd/profiling.rs
similarity index 76%
rename from crates/agentfs-cli/src/cmd/profiling.rs
rename to crates/vfs-cli/src/cmd/profiling.rs
index 7e007758..4a038ece 100644
--- a/crates/agentfs-cli/src/cmd/profiling.rs
+++ b/crates/vfs-cli/src/cmd/profiling.rs
@@ -3,7 +3,7 @@
//! Adapter-specific counters register from their owning crates when those
//! adapters are constructed; the CLI owns only the process report lifecycle.
-const PROFILE_SUMMARY_EVENT: &str = "agentfs_profile_summary";
+const PROFILE_SUMMARY_EVENT: &str = "vfs_profile_summary";
/// Drop guard installed by the CLI binary for a single process summary.
#[derive(Debug)]
@@ -35,15 +35,15 @@ pub fn emit_cli_report() {
emit_profile_summary("cli");
}
+#[cfg(target_os = "linux")]
pub(crate) fn report_checkpoint() {
- if let Some(payload) = agentfs_core::telemetry::checkpoint_payload(PROFILE_SUMMARY_EVENT) {
+ if let Some(payload) = vfs_core::telemetry::checkpoint_payload(PROFILE_SUMMARY_EVENT) {
eprintln!("{payload}");
}
}
fn emit_profile_summary(source: &str) {
- if let Some(payload) =
- agentfs_core::telemetry::take_summary_payload(PROFILE_SUMMARY_EVENT, source)
+ if let Some(payload) = vfs_core::telemetry::take_summary_payload(PROFILE_SUMMARY_EVENT, source)
{
eprintln!("{payload}");
}
diff --git a/crates/agentfs-cli/src/cmd/ps.rs b/crates/vfs-cli/src/cmd/ps.rs
similarity index 90%
rename from crates/agentfs-cli/src/cmd/ps.rs
rename to crates/vfs-cli/src/cmd/ps.rs
index 3ab6c5b7..304f6bdc 100644
--- a/crates/agentfs-cli/src/cmd/ps.rs
+++ b/crates/vfs-cli/src/cmd/ps.rs
@@ -1,4 +1,4 @@
-//! List active agentfs run sessions.
+//! List active vfs run sessions.
use std::io::Write;
use std::path::{Path, PathBuf};
@@ -23,21 +23,29 @@ pub struct ProcInfo {
}
/// Get the path to the procs directory for a session.
+///
+/// Proc files are written only by the Linux `vfs run` path; other platforms
+/// read sessions via [`list_sessions`] without these helpers.
+#[cfg(target_os = "linux")]
pub(crate) fn procs_dir(session_id: &str) -> PathBuf {
let home = dirs::home_dir().expect("home directory");
- home.join(".agentfs")
- .join("run")
- .join(session_id)
- .join("procs")
+ home.join(".vfs").join("run").join(session_id).join("procs")
}
/// Get the path to a proc file.
+#[cfg(target_os = "linux")]
fn proc_file(session_id: &str, pid: u32) -> PathBuf {
procs_dir(session_id).join(format!("{}.json", pid))
}
/// Write a proc file for the current process.
-pub(crate) fn write_proc_file(session_id: &str, owner: bool, command: &str, cwd: &Path) -> Result<()> {
+#[cfg(target_os = "linux")]
+pub(crate) fn write_proc_file(
+ session_id: &str,
+ owner: bool,
+ command: &str,
+ cwd: &Path,
+) -> Result<()> {
let pid = std::process::id();
let procs_dir = procs_dir(session_id);
std::fs::create_dir_all(&procs_dir)?;
@@ -58,6 +66,7 @@ pub(crate) fn write_proc_file(session_id: &str, owner: bool, command: &str, cwd:
}
/// Remove the proc file for the current process.
+#[cfg(target_os = "linux")]
pub(crate) fn remove_proc_file(session_id: &str) {
let pid = std::process::id();
let path = proc_file(session_id, pid);
@@ -74,7 +83,7 @@ fn is_process_alive(pid: u32) -> bool {
result == 0 || std::io::Error::last_os_error().raw_os_error() == Some(libc::EPERM)
}
-/// `agentfs run` sessions only exist on Unix platforms, so nothing can be
+/// `vfs run` sessions only exist on Unix platforms, so nothing can be
/// alive here.
#[cfg(not(unix))]
fn is_process_alive(_pid: u32) -> bool {
@@ -88,6 +97,7 @@ struct SessionInfo {
}
/// Get the set of active session IDs.
+#[cfg(target_os = "linux")]
pub(crate) fn active_session_ids() -> std::collections::HashSet {
list_sessions().into_iter().map(|s| s.session_id).collect()
}
@@ -142,7 +152,7 @@ fn list_sessions() -> Vec {
None => return vec![],
};
- let run_dir = home.join(".agentfs").join("run");
+ let run_dir = home.join(".vfs").join("run");
let entries = match std::fs::read_dir(&run_dir) {
Ok(e) => e,
Err(_) => return vec![],
@@ -215,12 +225,12 @@ const COL_OWNER: usize = 5;
const COL_COMMAND: usize = 15;
const COL_STARTED: usize = 10;
-/// List active agentfs run sessions.
+/// List active vfs run sessions.
pub fn list_ps(out: &mut W) -> Result<()> {
let sessions = list_sessions();
if sessions.is_empty() {
- writeln!(out, "No active agentfs run sessions.")?;
+ writeln!(out, "No active vfs run sessions.")?;
return Ok(());
}
diff --git a/crates/agentfs-cli/src/cmd/run/darwin.rs b/crates/vfs-cli/src/cmd/run/darwin.rs
similarity index 93%
rename from crates/agentfs-cli/src/cmd/run/darwin.rs
rename to crates/vfs-cli/src/cmd/run/darwin.rs
index 11c80d6d..612f5750 100644
--- a/crates/agentfs-cli/src/cmd/run/darwin.rs
+++ b/crates/vfs-cli/src/cmd/run/darwin.rs
@@ -2,7 +2,7 @@
//!
//! This module provides a sandboxed execution environment using NFS for
//! filesystem mounting. The current working directory becomes a
-//! copy-on-write overlay backed by AgentFS, mounted via a localhost NFS server.
+//! copy-on-write overlay backed by Vfs, mounted via a localhost NFS server.
//!
//! Sandboxing is enforced using macOS sandbox-exec with dynamically generated
//! profiles. Writes are restricted to the NFS mountpoint and allowed paths;
@@ -11,16 +11,16 @@
//! `PLATFORM_READ_ROOTS`).
use crate::opts::RunOptions;
-use agentfs_core::{AgentFS, AgentFSOptions, FileSystem, HostFS, OverlayFS};
use anyhow::{Context, Result};
use std::path::{Path, PathBuf};
use std::sync::Arc;
+use vfs_core::{FileSystem, HostFS, OverlayFS, Vfs, VfsOptions};
-use agentfs_mount::supervise::{
+use vfs_mount::supervise::{
exit_code_for_spawn_error, exit_code_for_status, run_supervised, supervise_command,
ChildOutcome,
};
-use agentfs_mount::{mount_fs, Backend, MountOpts};
+use vfs_mount::{mount_fs, Backend, MountOpts};
/// Configuration for the macOS sandbox profile.
#[derive(Debug, Clone)]
@@ -139,7 +139,7 @@ impl SandboxParams {
/// comment) where Seatbelt parameters are not available, so restrict it to a
/// conservative charset instead of trusting `--session` input. A session id
/// with no surviving characters falls back to a fixed placeholder so the
-/// deny message never degrades to a bare `agentfs-:` tag.
+/// deny message never degrades to a bare `vfs-:` tag.
fn sandbox_log_tag(session_id: &str) -> String {
let tag: String = session_id
.chars()
@@ -152,7 +152,7 @@ fn sandbox_log_tag(session_id: &str) -> String {
}
}
-/// Generate a sandbox-exec profile for AgentFS.
+/// Generate a sandbox-exec profile for Vfs.
///
/// The profile allows most operations but restricts file writes to the NFS
/// mountpoint, temp directories, and explicitly allowed paths, and file
@@ -162,7 +162,7 @@ fn sandbox_log_tag(session_id: &str) -> String {
pub(super) fn generate_sandbox_profile(config: &SandboxConfig) -> SandboxProfile {
let mut profile = Vec::new();
let mut params = SandboxParams::default();
- let log_tag = format!("agentfs-{}", sandbox_log_tag(&config.session_id));
+ let log_tag = format!("vfs-{}", sandbox_log_tag(&config.session_id));
profile.push("(version 1)".to_string());
profile.push(format!(
@@ -319,36 +319,36 @@ pub async fn run(options: RunOptions) -> Result<()> {
std::process::exit(exit_code_for_outcome(outcome));
} else {
eprintln!("Cleaning up stale NFS mount...");
- if let Err(e) = agentfs_mount::unmount(&session.mountpoint, Backend::Nfs, true) {
+ if let Err(e) = vfs_mount::unmount(&session.mountpoint, Backend::Nfs, true) {
eprintln!("Warning: Failed to unmount stale mount: {}", e);
}
}
}
- // Initialize the AgentFS database
+ // Initialize the Vfs database
let db_path_str = session
.db_path
.to_str()
.context("Database path contains non-UTF8 characters")?;
let encrypted = encryption.is_some();
- let mut options = AgentFSOptions::with_path(db_path_str)
- .with_core_config(crate::config::core_config_from_env());
+ let mut options =
+ VfsOptions::with_path(db_path_str).with_core_config(crate::config::core_config_from_env());
if let Some(encryption) = encryption {
options = options.with_encryption(encryption);
}
- let agentfs = AgentFS::open(options)
+ let vfs = Vfs::open(options)
.await
.map_err(|err| crate::cmd::migrate::open_error_with_guidance(err, db_path_str))
- .context("Failed to create AgentFS")?;
+ .context("Failed to create Vfs")?;
// Create overlay filesystem with CWD as base
let base_str = cwd.to_string_lossy().to_string();
let hostfs = HostFS::new(&base_str).context("Failed to create HostFS")?;
let overlay = if let Some(policy) = partial_origin_policy {
- OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), agentfs.fs, policy)
+ OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), vfs.fs, policy)
} else {
- OverlayFS::new(Arc::new(hostfs), agentfs.fs)
+ OverlayFS::new(Arc::new(hostfs), vfs.fs)
};
// Initialize the overlay (copies directory structure)
@@ -359,10 +359,14 @@ pub async fn run(options: RunOptions) -> Result<()> {
let fs: Arc = Arc::new(overlay);
- let mut mount_opts = MountOpts::new(session.mountpoint.clone(), Backend::Nfs);
- mount_opts.fsname = format!("agentfs:{}", session.session_id);
- mount_opts.lazy_unmount = true;
- mount_opts.timeout = std::time::Duration::from_secs(10);
+ let mount_opts = MountOpts {
+ mountpoint: session.mountpoint.clone(),
+ backend: Backend::Nfs,
+ fsname: format!("vfs:{}", session.session_id),
+ lazy_unmount: true,
+ timeout: std::time::Duration::from_secs(10),
+ ..MountOpts::default()
+ };
let mount_handle = mount_fs(fs, mount_opts).await?;
print_welcome_banner(&session, encrypted);
@@ -383,10 +387,10 @@ pub async fn run(options: RunOptions) -> Result<()> {
eprintln!("Session: {}", session.session_id);
eprintln!();
eprintln!("To resume this session:");
- eprintln!(" agentfs run --session {}", session.session_id);
+ eprintln!(" vfs run --session {}", session.session_id);
eprintln!();
eprintln!("To see what changed:");
- eprintln!(" agentfs diff {}", session.session_id);
+ eprintln!(" vfs diff {}", session.session_id);
crate::profiling::emit_cli_report();
match status {
@@ -415,7 +419,7 @@ fn exit_code_for_outcome(outcome: ChildOutcome) -> i32 {
fn print_welcome_banner(session: &RunSession, encrypted: bool) {
use super::group_paths_by_parent;
- eprintln!("Welcome to AgentFS!");
+ eprintln!("Welcome to Vfs!");
eprintln!();
eprintln!("The following directories are writable:");
eprintln!();
@@ -433,7 +437,7 @@ fn print_welcome_banner(session: &RunSession, encrypted: bool) {
eprintln!();
eprintln!("To join this session from another terminal:");
eprintln!();
- eprintln!(" agentfs run --session {} ", session.session_id);
+ eprintln!(" vfs run --session {} ", session.session_id);
eprintln!();
}
@@ -465,7 +469,7 @@ fn setup_run_directory(
home: &Path,
) -> Result {
let run_id = session_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
- let run_dir = home.join(".agentfs").join("run").join(&run_id);
+ let run_dir = home.join(".vfs").join("run").join(&run_id);
std::fs::create_dir_all(&run_dir).context("Failed to create run directory")?;
let db_path = run_dir.join("delta.db");
@@ -555,8 +559,8 @@ fn command_in_mount(
cmd.arg(&command)
.args(&args)
.current_dir(&session.mountpoint)
- .env("AGENTFS", "1")
- .env("AGENTFS_SANDBOX", "macos-sandbox")
+ .env("VFS", "1")
+ .env("VFS_SANDBOX", "macos-sandbox")
// Bash prompt - show full path since we're not changing HOME
.env("PS1", "🤖 \\w\\$ ")
// Zsh: use custom ZDOTDIR to override prompt
diff --git a/crates/agentfs-cli/src/cmd/run/linux.rs b/crates/vfs-cli/src/cmd/run/linux.rs
similarity index 95%
rename from crates/agentfs-cli/src/cmd/run/linux.rs
rename to crates/vfs-cli/src/cmd/run/linux.rs
index 98533ffe..acfc316c 100644
--- a/crates/agentfs-cli/src/cmd/run/linux.rs
+++ b/crates/vfs-cli/src/cmd/run/linux.rs
@@ -2,7 +2,7 @@
//!
//! This module provides a sandboxed execution environment where the current
//! working directory becomes a copy-on-write overlay, and the rest of the
-//! filesystem is read-only. All modifications are captured in an AgentFS
+//! filesystem is read-only. All modifications are captured in a Vfs
//! database, leaving the original files untouched.
//!
//! The implementation mounts a FUSE filesystem on a hidden temporary directory,
@@ -17,9 +17,6 @@
use super::{default_allowed_paths, group_paths_by_parent};
use crate::opts::RunOptions;
-use agentfs_core::{
- AgentFS, AgentFSOptions, EncryptionConfig, HostFS, OverlayFS, PartialOriginPolicy,
-};
use anyhow::{bail, Context, Result};
use std::{
cmp::Reverse,
@@ -32,8 +29,9 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
+use vfs_core::{EncryptionConfig, HostFS, OverlayFS, PartialOriginPolicy, Vfs, VfsOptions};
-use agentfs_mount::{is_mountpoint, mount_fs, Backend, MountHandle, MountOpts};
+use vfs_mount::{is_mountpoint, mount_fs, Backend, MountHandle, MountOpts};
/// Exit code returned when exec fails (standard shell convention for "command not found")
const EXIT_COMMAND_NOT_FOUND: i32 = 127;
@@ -219,7 +217,7 @@ pub fn run(options: RunOptions) -> Result<()> {
}
/// Record the completed run session in the delta database's tool-call audit
-/// log so `agentfs timeline ` reports the sandboxed command and
+/// log so `vfs timeline ` reports the sandboxed command and
/// its exit status (VAL-CROSS-006).
async fn record_run_audit(
db_path: &Path,
@@ -233,12 +231,12 @@ async fn record_run_audit(
let db_path_str = db_path
.to_str()
.context("Database path contains non-UTF8 characters")?;
- let mut options = AgentFSOptions::with_path(db_path_str)
- .with_core_config(crate::config::core_config_from_env());
+ let mut options =
+ VfsOptions::with_path(db_path_str).with_core_config(crate::config::core_config_from_env());
if let Some(encryption) = encryption {
options = options.with_encryption(encryption);
}
- let agentfs = AgentFS::open(options)
+ let vfs = Vfs::open(options)
.await
.map_err(|err| crate::cmd::migrate::open_error_with_guidance(err, db_path_str))
.context("Failed to reopen delta database for the audit row")?;
@@ -249,8 +247,7 @@ async fn record_run_audit(
let result = serde_json::json!({ "exit_code": exit_code });
let error_text = (exit_code != 0).then(|| format!("exit code {exit_code}"));
- agentfs
- .tools
+ vfs.tools
.record(
"run",
started_at_ms,
@@ -265,8 +262,7 @@ async fn record_run_audit(
// Leave the DB in single-file journal mode again: without this the audit
// insert sits in a fresh WAL sidecar, undoing the clean teardown the
// supervised session just performed.
- agentfs
- .fs
+ vfs.fs
.finalize()
.await
.context("Failed to finalize the delta database after the audit row")?;
@@ -303,15 +299,15 @@ async fn mount_session_fs(
.db_path
.to_str()
.context("Database path contains non-UTF8 characters")?;
- let mut options = AgentFSOptions::with_path(db_path_str)
- .with_core_config(crate::config::core_config_from_env());
+ let mut options =
+ VfsOptions::with_path(db_path_str).with_core_config(crate::config::core_config_from_env());
if let Some(encryption) = encryption {
options = options.with_encryption(encryption);
}
- let agentfs = AgentFS::open(options)
+ let vfs = Vfs::open(options)
.await
.map_err(|err| crate::cmd::migrate::open_error_with_guidance(err, db_path_str))
- .context("Failed to create delta AgentFS")?;
+ .context("Failed to create delta Vfs")?;
let hostfs = HostFS::new(&fd_path).context("Failed to create HostFS")?;
#[cfg(target_family = "unix")]
@@ -324,9 +320,9 @@ async fn mount_session_fs(
let base = Arc::new(hostfs);
let overlay = if let Some(policy) = partial_origin_policy {
- OverlayFS::new_with_partial_origin_policy(base, agentfs.fs, policy)
+ OverlayFS::new_with_partial_origin_policy(base, vfs.fs, policy)
} else {
- OverlayFS::new(base, agentfs.fs)
+ OverlayFS::new(base, vfs.fs)
};
let cwd_str = cwd
@@ -344,7 +340,7 @@ async fn mount_session_fs(
let mount_opts = MountOpts {
mountpoint: session.fuse_mountpoint.clone(),
backend: Backend::Fuse,
- fsname: format!("agentfs:{}", session.run_id),
+ fsname: format!("vfs:{}", session.run_id),
uid: Some(uid),
gid: Some(gid),
allow_other: system,
@@ -436,14 +432,14 @@ fn run_in_existing_session(
}
let rt = crate::get_runtime();
- let status = rt.block_on(agentfs_mount::supervise::supervise_pid_with_hooks(
+ let status = rt.block_on(vfs_mount::supervise::supervise_pid_with_hooks(
child_pid,
- agentfs_mount::supervise::SuperviseOpts::default(),
- agentfs_mount::supervise::SuperviseHooks::with_profile_checkpoint(
+ vfs_mount::supervise::SuperviseOpts::default(),
+ vfs_mount::supervise::SuperviseHooks::with_profile_checkpoint(
crate::profiling::report_checkpoint,
),
))?;
- let exit_code = agentfs_mount::supervise::exit_code_for_status(status);
+ let exit_code = vfs_mount::supervise::exit_code_for_status(status);
// Clean up proc file
crate::cmd::ps::remove_proc_file(session_id);
@@ -455,7 +451,7 @@ fn run_in_existing_session(
/// Print the welcome banner showing sandbox configuration.
fn print_welcome_banner(cwd: &Path, allowed_paths: &[PathBuf], session_id: &str, encrypted: bool) {
- eprintln!("Welcome to AgentFS!");
+ eprintln!("Welcome to Vfs!");
eprintln!();
eprintln!("The following directories are writable:");
eprintln!();
@@ -472,7 +468,7 @@ fn print_welcome_banner(cwd: &Path, allowed_paths: &[PathBuf], session_id: &str,
eprintln!();
eprintln!("To join this session from another terminal:");
eprintln!();
- eprintln!(" agentfs run --session {} ", session_id);
+ eprintln!(" vfs run --session {} ", session_id);
eprintln!();
}
@@ -495,7 +491,7 @@ struct RunSession {
fn setup_run_directory(session_id: Option) -> Result {
let run_id = session_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
let home_dir = dirs::home_dir().context("Failed to get home directory")?;
- let run_dir = home_dir.join(".agentfs").join("run").join(&run_id);
+ let run_dir = home_dir.join(".vfs").join("run").join(&run_id);
std::fs::create_dir_all(&run_dir).context("Failed to create run directory")?;
let db_path = run_dir.join("delta.db");
@@ -652,7 +648,7 @@ fn run_child(
pipe_from_parent: libc::c_int,
pipe_to_parent: libc::c_int,
) -> ! {
- if let Err(error) = agentfs_mount::supervise::prepare_forked_child(true) {
+ if let Err(error) = vfs_mount::supervise::prepare_forked_child(true) {
child_exit(&format!("Failed to prepare supervised child: {error}"));
}
@@ -709,7 +705,7 @@ fn run_child(
if !is_mountpoint(fuse_mountpoint) {
child_exit(&format!(
"FUSE overlay never appeared at {} inside the sandbox mount namespace.\n\
- agentfs run requires shared mount propagation on / (the systemd default);\n\
+ vfs run requires shared mount propagation on / (the systemd default);\n\
this host appears to use private propagation (check with\n\
`findmnt -o TARGET,PROPAGATION /`). Enable it with `mount --make-rshared /`\n\
and retry.",
@@ -1144,11 +1140,11 @@ async fn run_parent(child_pid: i32, mount_handle: MountHandle, session_id: &str)
// Get mountpoint before dropping handle
let fuse_mountpoint = mount_handle.mountpoint().to_path_buf();
- let status = agentfs_mount::supervise::run_supervised_pid_with_hooks(
+ let status = vfs_mount::supervise::run_supervised_pid_with_hooks(
mount_handle,
child_pid,
- agentfs_mount::supervise::SuperviseOpts::default(),
- agentfs_mount::supervise::SuperviseHooks::with_profile_checkpoint(
+ vfs_mount::supervise::SuperviseOpts::default(),
+ vfs_mount::supervise::SuperviseHooks::with_profile_checkpoint(
crate::profiling::report_checkpoint,
),
)
@@ -1175,14 +1171,14 @@ async fn run_parent(child_pid: i32, mount_handle: MountHandle, session_id: &str)
eprintln!("Session: {}", session_id);
eprintln!();
eprintln!("To resume this session:");
- eprintln!(" agentfs run --session {}", session_id);
+ eprintln!(" vfs run --session {}", session_id);
eprintln!();
eprintln!("To see what changed:");
- eprintln!(" agentfs diff {}", session_id);
+ eprintln!(" vfs diff {}", session_id);
crate::profiling::emit_cli_report();
- Ok(agentfs_mount::supervise::exit_code_for_status(status?))
+ Ok(vfs_mount::supervise::exit_code_for_status(status?))
}
/// Execute the command, replacing the current process.
@@ -1225,8 +1221,7 @@ fn exec_command(command: PathBuf, args: Vec, session_id: &str) -> ! {
let error = std::io::Error::last_os_error();
child_exit_with_code(
&format!("Error: Failed to execute {}: {}", command.display(), error),
- agentfs_mount::supervise::exit_code_for_spawn_error(&error)
- .unwrap_or(EXIT_COMMAND_NOT_FOUND),
+ vfs_mount::supervise::exit_code_for_spawn_error(&error).unwrap_or(EXIT_COMMAND_NOT_FOUND),
);
}
@@ -1236,9 +1231,9 @@ fn setup_env_vars(session_id: &str) {
// sandboxed command must see the user's TMPDIR instead (inside the
// namespace /tmp is a fresh scoping tmpfs anyway).
crate::config::restore_original_tmpdir_env();
- std::env::set_var("AGENTFS", "1");
- std::env::set_var("AGENTFS_SANDBOX", "linux-namespace");
- std::env::set_var("AGENTFS_SESSION", session_id);
+ std::env::set_var("VFS", "1");
+ std::env::set_var("VFS_SANDBOX", "linux-namespace");
+ std::env::set_var("VFS_SESSION", session_id);
std::env::set_var("PS1", "🤖 \\u@\\h:\\w\\$ ");
// Configure SSH to skip system config files.
diff --git a/crates/agentfs-cli/src/cmd/run/mod.rs b/crates/vfs-cli/src/cmd/run/mod.rs
similarity index 100%
rename from crates/agentfs-cli/src/cmd/run/mod.rs
rename to crates/vfs-cli/src/cmd/run/mod.rs
diff --git a/crates/agentfs-cli/src/cmd/run/not_supported.rs b/crates/vfs-cli/src/cmd/run/not_supported.rs
similarity index 100%
rename from crates/agentfs-cli/src/cmd/run/not_supported.rs
rename to crates/vfs-cli/src/cmd/run/not_supported.rs
diff --git a/crates/agentfs-cli/src/cmd/run/tests.rs b/crates/vfs-cli/src/cmd/run/tests.rs
similarity index 95%
rename from crates/agentfs-cli/src/cmd/run/tests.rs
rename to crates/vfs-cli/src/cmd/run/tests.rs
index ea8c7fa0..e598522a 100644
--- a/crates/agentfs-cli/src/cmd/run/tests.rs
+++ b/crates/vfs-cli/src/cmd/run/tests.rs
@@ -161,7 +161,7 @@ mod darwin_read_scoping {
fn config() -> SandboxConfig {
SandboxConfig {
- mountpoint: PathBuf::from("/Users/tester/.agentfs/run/sess-1/mnt"),
+ mountpoint: PathBuf::from("/Users/tester/.vfs/run/sess-1/mnt"),
allow_paths: vec![
PathBuf::from("/Users/tester/.codex"),
PathBuf::from("/Users/tester/.claude.json"),
@@ -232,8 +232,8 @@ mod darwin_read_scoping {
let profile = generate_sandbox_profile(&config());
for path in [
- "/Users/tester/.agentfs/run/sess-1/mnt",
- "/Users/tester/.agentfs/run/sess-1",
+ "/Users/tester/.vfs/run/sess-1/mnt",
+ "/Users/tester/.vfs/run/sess-1",
"/Users/tester/.codex",
"/Users/tester/.claude.json",
] {
@@ -255,9 +255,9 @@ mod darwin_read_scoping {
for parent in [
"/Users",
"/Users/tester",
- "/Users/tester/.agentfs",
- "/Users/tester/.agentfs/run",
- "/Users/tester/.agentfs/run/sess-1",
+ "/Users/tester/.vfs",
+ "/Users/tester/.vfs/run",
+ "/Users/tester/.vfs/run/sess-1",
] {
let name = param_for(&profile, parent);
let rule = format!(
@@ -309,8 +309,8 @@ mod darwin_read_scoping {
fn write_scoping_is_unchanged() {
let profile = generate_sandbox_profile(&config());
- let mountpoint = param_for(&profile, "/Users/tester/.agentfs/run/sess-1/mnt");
- let run_dir = param_for(&profile, "/Users/tester/.agentfs/run/sess-1");
+ let mountpoint = param_for(&profile, "/Users/tester/.vfs/run/sess-1/mnt");
+ let run_dir = param_for(&profile, "/Users/tester/.vfs/run/sess-1");
let codex = param_for(&profile, "/Users/tester/.codex");
for rule in [
format!(r#"(allow file-write* (subpath (param "{mountpoint}")))"#),
@@ -375,7 +375,7 @@ mod darwin_read_scoping {
assert!(
profile.policy.contains(
- r#"(deny default (with message "agentfs-evilallowfile-readdenysignalx: access denied"))"#
+ r#"(deny default (with message "vfs-evilallowfile-readdenysignalx: access denied"))"#
),
"session id must be reduced to a conservative charset:\n{}",
profile.policy
@@ -399,12 +399,12 @@ mod darwin_read_scoping {
assert!(
profile
.policy
- .contains(r#"(deny default (with message "agentfs-session: access denied"))"#),
+ .contains(r#"(deny default (with message "vfs-session: access denied"))"#),
"a session id sanitized to nothing must fall back to a fixed tag:\n{}",
profile.policy
);
assert!(
- !profile.policy.contains("agentfs-:"),
+ !profile.policy.contains("vfs-:"),
"the log tag must never be empty:\n{}",
profile.policy
);
diff --git a/crates/agentfs-cli/src/cmd/safety.rs b/crates/vfs-cli/src/cmd/safety.rs
similarity index 96%
rename from crates/agentfs-cli/src/cmd/safety.rs
rename to crates/vfs-cli/src/cmd/safety.rs
index 3e1fdfbb..6512ad41 100644
--- a/crates/agentfs-cli/src/cmd/safety.rs
+++ b/crates/vfs-cli/src/cmd/safety.rs
@@ -1,6 +1,5 @@
-//! Production safety commands for local AgentFS databases.
+//! Production safety commands for local Vfs databases.
-use agentfs_core::{schema::integrity, AgentFSOptions};
use anyhow::{Context, Result as AnyhowResult};
use std::collections::BTreeMap;
use std::fs;
@@ -8,6 +7,7 @@ use std::io::{Read, Seek, SeekFrom, Write};
use std::path::{Component, Path, PathBuf};
use turso::transaction::{Transaction, TransactionBehavior};
use turso::{Builder, Connection, EncryptionOpts, Value};
+use vfs_core::{schema::integrity, VfsOptions};
const S_IFMT: i64 = 0o170000;
const S_IFREG: i64 = 0o100000;
@@ -28,7 +28,7 @@ struct PartialOriginRow {
base_ctime_nsec: i64,
}
-/// Run integrity and schema-invariant checks for a local AgentFS database.
+/// Run integrity and schema-invariant checks for a local Vfs database.
pub async fn handle_integrity_command(
stdout: &mut impl Write,
id_or_path: String,
@@ -113,7 +113,7 @@ async fn check_integrity_readonly(
Ok(report)
}
-/// Create a portable main-database backup of a local AgentFS database.
+/// Create a portable main-database backup of a local Vfs database.
pub async fn handle_backup_command(
stdout: &mut impl Write,
id_or_path: String,
@@ -185,7 +185,7 @@ pub async fn handle_backup_command(
Ok(())
}
-/// Create a portable materialized copy of a local AgentFS database.
+/// Create a portable materialized copy of a local Vfs database.
pub async fn handle_materialize_command(
stdout: &mut impl Write,
id_or_path: String,
@@ -847,7 +847,7 @@ fn write_integrity_report(
}
fn resolve_local_db_path(id_or_path: &str) -> AnyhowResult {
- let options = AgentFSOptions::resolve(id_or_path)?;
+ let options = VfsOptions::resolve(id_or_path)?;
let db_path = options
.db_path()
.context("Failed to resolve database path")?;
@@ -1028,10 +1028,10 @@ fn value_i64(value: Value) -> AnyhowResult {
#[cfg(test)]
mod tests {
use super::*;
- use agentfs_core::{AgentFS, AgentFSOptions};
use serde_json::Value as JsonValue;
+ use vfs_core::{Vfs, VfsOptions};
- async fn write_agent_file(agent: &AgentFS, path: &str, data: &[u8]) {
+ async fn write_agent_file(agent: &Vfs, path: &str, data: &[u8]) {
let (_, file) = agent
.fs
.create_file(path, (S_IFREG | 0o644) as u32, 0, 0)
@@ -1058,7 +1058,7 @@ mod tests {
let source = temp_dir.path().join("source.db");
fs::write(&source, b"stub").unwrap();
- let bare = format!("agentfs-backup-target-{}.db", std::process::id());
+ let bare = format!("vfs-backup-target-{}.db", std::process::id());
ensure_backup_target(&source, Path::new(&bare)).expect(
"a bare relative backup target must resolve its parent to the current directory",
);
@@ -1083,7 +1083,7 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("valid.db");
{
- let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy()))
.await
.unwrap();
write_agent_file(&agent, "/hello.txt", b"hello").await;
@@ -1110,7 +1110,7 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("read-only.db");
{
- let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy()))
.await
.unwrap();
write_agent_file(&agent, "/hello.txt", b"hello").await;
@@ -1152,7 +1152,7 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("hard-corrupt.db");
{
- let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy()))
.await
.unwrap();
write_agent_file(&agent, "/hello.txt", b"hello").await;
@@ -1188,7 +1188,7 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("stale-shm.db");
{
- let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy()))
.await
.unwrap();
write_agent_file(&agent, "/hello.txt", b"hello").await;
@@ -1246,7 +1246,7 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("single-file.db");
{
- let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy()))
.await
.unwrap();
write_agent_file(&agent, "/hello.txt", b"hello").await;
@@ -1284,7 +1284,7 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("corrupt.db");
{
- let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy()))
.await
.unwrap();
write_agent_file(&agent, "/bad.txt", b"bad").await;
@@ -1333,7 +1333,7 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("orphan.db");
{
- let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy()))
.await
.unwrap();
let conn = agent.get_connection().await.unwrap();
@@ -1375,7 +1375,7 @@ mod tests {
let target = temp_dir.path().join("backup.db");
let large = vec![7_u8; 128 * 1024 + 3];
{
- let agent = AgentFS::open(AgentFSOptions::with_path(source.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(source.to_string_lossy()))
.await
.unwrap();
write_agent_file(&agent, "/small.txt", b"small").await;
@@ -1395,7 +1395,7 @@ mod tests {
.unwrap();
assert!(target.is_file());
- let backup = AgentFS::open(AgentFSOptions::with_path(target.to_string_lossy()))
+ let backup = Vfs::open(VfsOptions::with_path(target.to_string_lossy()))
.await
.unwrap();
assert_eq!(
@@ -1418,9 +1418,8 @@ mod tests {
let key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
let cipher = "aegis256";
{
- let agent = AgentFS::open(
- AgentFSOptions::with_path(source.to_string_lossy())
- .with_encryption_key(key, cipher),
+ let agent = Vfs::open(
+ VfsOptions::with_path(source.to_string_lossy()).with_encryption_key(key, cipher),
)
.await
.unwrap();
@@ -1455,8 +1454,8 @@ mod tests {
.await
.unwrap();
- let backup = AgentFS::open(
- AgentFSOptions::with_path(target.to_string_lossy()).with_encryption_key(key, cipher),
+ let backup = Vfs::open(
+ VfsOptions::with_path(target.to_string_lossy()).with_encryption_key(key, cipher),
)
.await
.unwrap();
@@ -1472,7 +1471,7 @@ mod tests {
let source = temp_dir.path().join("partial.db");
let target = temp_dir.path().join("partial-backup.db");
{
- let agent = AgentFS::open(AgentFSOptions::with_path(source.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(source.to_string_lossy()))
.await
.unwrap();
let conn = agent.get_connection().await.unwrap();
@@ -1562,7 +1561,7 @@ mod tests {
let fingerprint = metadata_fingerprint(&fs::metadata(&base_file).unwrap());
let expected = b"abcdWXYZij".to_vec();
- let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy()))
.await
.unwrap();
let conn = agent.get_connection().await.unwrap();
@@ -1639,7 +1638,7 @@ mod tests {
assert_eq!(partial_table_count(db_path, "fs_partial_origin").await, 0);
assert_eq!(partial_table_count(db_path, "fs_chunk_override").await, 0);
- let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy()))
+ let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy()))
.await
.unwrap();
assert_eq!(
diff --git a/crates/agentfs-cli/src/cmd/sync.rs b/crates/vfs-cli/src/cmd/sync.rs
similarity index 77%
rename from crates/agentfs-cli/src/cmd/sync.rs
rename to crates/vfs-cli/src/cmd/sync.rs
index deb3e6fc..4ed8484c 100644
--- a/crates/agentfs-cli/src/cmd/sync.rs
+++ b/crates/vfs-cli/src/cmd/sync.rs
@@ -1,12 +1,12 @@
-use agentfs_core::AgentFSOptions;
+use vfs_core::VfsOptions;
-use crate::cmd::init::open_agentfs;
+use crate::cmd::init::open_vfs;
pub async fn handle_pull_command(id_or_path: String) -> anyhow::Result<()> {
- let options = AgentFSOptions::resolve(&id_or_path)?;
+ let options = VfsOptions::resolve(&id_or_path)?;
eprintln!("Using agent: {}", id_or_path);
- let agent = open_agentfs(options)
+ let agent = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
agent.pull().await?;
@@ -15,10 +15,10 @@ pub async fn handle_pull_command(id_or_path: String) -> anyhow::Result<()> {
}
pub async fn handle_push_command(id_or_path: String) -> anyhow::Result<()> {
- let options = AgentFSOptions::resolve(&id_or_path)?;
+ let options = VfsOptions::resolve(&id_or_path)?;
eprintln!("Using agent: {}", id_or_path);
- let agent = open_agentfs(options)
+ let agent = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
agent.push().await?;
@@ -27,10 +27,10 @@ pub async fn handle_push_command(id_or_path: String) -> anyhow::Result<()> {
}
pub async fn handle_checkpoint_command(id_or_path: String) -> anyhow::Result<()> {
- let options = AgentFSOptions::resolve(&id_or_path)?;
+ let options = VfsOptions::resolve(&id_or_path)?;
eprintln!("Using agent: {}", id_or_path);
- let agent = open_agentfs(options)
+ let agent = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
agent.checkpoint().await?;
@@ -42,10 +42,10 @@ pub async fn handle_stats_command(
stdout: &mut impl std::io::Write,
id_or_path: String,
) -> anyhow::Result<()> {
- let options = AgentFSOptions::resolve(&id_or_path)?;
+ let options = VfsOptions::resolve(&id_or_path)?;
eprintln!("Using agent: {}", id_or_path);
- let agent = open_agentfs(options)
+ let agent = open_vfs(options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?;
let result: anyhow::Result<()> = async {
diff --git a/crates/agentfs-cli/src/cmd/timeline.rs b/crates/vfs-cli/src/cmd/timeline.rs
similarity index 84%
rename from crates/agentfs-cli/src/cmd/timeline.rs
rename to crates/vfs-cli/src/cmd/timeline.rs
index 64e9aa9d..c3a4bea7 100644
--- a/crates/agentfs-cli/src/cmd/timeline.rs
+++ b/crates/vfs-cli/src/cmd/timeline.rs
@@ -1,10 +1,10 @@
-use agentfs_core::{toolcalls::ToolCall, AgentFSOptions, ToolCalls};
use anyhow::{Context, Result as AnyhowResult};
use chrono::TimeZone;
use std::io::Write;
use std::str::FromStr;
+use vfs_core::{toolcalls::ToolCall, ToolCalls, VfsOptions};
-use crate::cmd::init::{finalize_readonly, open_agentfs};
+use crate::cmd::init::{finalize_readonly, open_vfs};
/// Output format for timeline display
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -40,23 +40,23 @@ pub async fn show_timeline(
id_or_path: &str,
options: &TimelineOptions,
) -> AnyhowResult<()> {
- let agent_options = AgentFSOptions::resolve(id_or_path)?;
+ let agent_options = VfsOptions::resolve(id_or_path)?;
- let agentfs = open_agentfs(agent_options)
+ let vfs = open_vfs(agent_options)
.await
.map_err(|err| super::migrate::open_error_with_guidance(err, id_or_path))?;
- let result = show_timeline_opened(stdout, &agentfs, options).await;
- finalize_readonly(&agentfs).await;
+ let result = show_timeline_opened(stdout, &vfs, options).await;
+ finalize_readonly(&vfs).await;
result
}
async fn show_timeline_opened(
stdout: &mut impl Write,
- agentfs: &agentfs_core::AgentFS,
+ vfs: &vfs_core::Vfs,
options: &TimelineOptions,
) -> AnyhowResult<()> {
- let toolcalls = ToolCalls::from_pool(agentfs.get_pool())
+ let toolcalls = ToolCalls::from_pool(vfs.get_pool())
.await
.context("Failed to create tool calls tracker")?;
@@ -150,16 +150,16 @@ fn format_json(stdout: &mut impl Write, calls: &[ToolCall]) -> AnyhowResult<()>
#[cfg(test)]
mod tests {
use super::*;
- use agentfs_core::{AgentFS, AgentFSOptions};
use tempfile::NamedTempFile;
+ use vfs_core::{Vfs, VfsOptions};
- async fn create_test_agentfs() -> (AgentFS, String, NamedTempFile) {
+ async fn create_test_vfs() -> (Vfs, String, NamedTempFile) {
let file = NamedTempFile::new().unwrap();
let path = file.path().to_str().unwrap();
- let agentfs = AgentFS::open(AgentFSOptions::with_path(path.to_string()))
+ let vfs = Vfs::open(VfsOptions::with_path(path.to_string()))
.await
.unwrap();
- (agentfs, file.path().to_str().unwrap().to_string(), file)
+ (vfs, file.path().to_str().unwrap().to_string(), file)
}
/// Create default TimelineOptions for testing
@@ -174,7 +174,7 @@ mod tests {
#[tokio::test]
async fn test_timeline_empty() {
- let (_agentfs, path, _file) = create_test_agentfs().await;
+ let (_vfs, path, _file) = create_test_vfs().await;
let mut buf = Vec::new();
show_timeline(&mut buf, &path, &default_options())
@@ -187,9 +187,9 @@ mod tests {
#[tokio::test]
async fn test_timeline_table_format() {
- let (agentfs, path, _file) = create_test_agentfs().await;
+ let (vfs, path, _file) = create_test_vfs().await;
- agentfs.tools.start("test_tool", None).await.unwrap();
+ vfs.tools.start("test_tool", None).await.unwrap();
let mut buf = Vec::new();
show_timeline(&mut buf, &path, &default_options())
@@ -205,10 +205,10 @@ mod tests {
#[tokio::test]
async fn test_timeline_filter_by_name() {
- let (agentfs, path, _file) = create_test_agentfs().await;
+ let (vfs, path, _file) = create_test_vfs().await;
- agentfs.tools.start("tool_a", None).await.unwrap();
- agentfs.tools.start("tool_b", None).await.unwrap();
+ vfs.tools.start("tool_a", None).await.unwrap();
+ vfs.tools.start("tool_b", None).await.unwrap();
let mut buf = Vec::new();
let options = TimelineOptions {
@@ -235,22 +235,21 @@ mod tests {
#[tokio::test]
async fn test_timeline_filter_by_status() {
- let (agentfs, path, _file) = create_test_agentfs().await;
+ let (vfs, path, _file) = create_test_vfs().await;
// Success call
- let success_id = agentfs.tools.start("test_tool", None).await.unwrap();
- agentfs
- .tools
+ let success_id = vfs.tools.start("test_tool", None).await.unwrap();
+ vfs.tools
.success(success_id, Some(serde_json::json!({"success": true})))
.await
.unwrap();
// Error call
- let error_id = agentfs.tools.start("test_tool", None).await.unwrap();
- agentfs.tools.error(error_id, "test error").await.unwrap();
+ let error_id = vfs.tools.start("test_tool", None).await.unwrap();
+ vfs.tools.error(error_id, "test error").await.unwrap();
// Pending call
- agentfs.tools.start("test_tool", None).await.unwrap();
+ vfs.tools.start("test_tool", None).await.unwrap();
// Test status="success" returns only successful calls
let mut buf = Vec::new();
@@ -294,15 +293,11 @@ mod tests {
#[tokio::test]
async fn test_timeline_limit() {
- let (agentfs, path, _file) = create_test_agentfs().await;
+ let (vfs, path, _file) = create_test_vfs().await;
// Record 5 tool calls
for i in 0..5 {
- agentfs
- .tools
- .start(&format!("tool_{}", i), None)
- .await
- .unwrap();
+ vfs.tools.start(&format!("tool_{}", i), None).await.unwrap();
}
// Test limit=2 returns exactly 2 calls
@@ -323,9 +318,9 @@ mod tests {
#[tokio::test]
async fn test_timeline_leaves_no_wal_sidecar() {
- let (agentfs, path, _file) = create_test_agentfs().await;
- agentfs.tools.start("test_tool", None).await.unwrap();
- drop(agentfs);
+ let (vfs, path, _file) = create_test_vfs().await;
+ vfs.tools.start("test_tool", None).await.unwrap();
+ drop(vfs);
let mut buf = Vec::new();
show_timeline(&mut buf, &path, &default_options())
@@ -349,11 +344,10 @@ mod tests {
#[tokio::test]
async fn test_timeline_started_at_units_consistent() {
- let (agentfs, path, _file) = create_test_agentfs().await;
+ let (vfs, path, _file) = create_test_vfs().await;
// tool_calls stores milliseconds; both display forms must expose the
// same second-granularity value.
- agentfs
- .tools
+ vfs.tools
.record(
"clock",
1_600_000_000_000,
@@ -389,11 +383,10 @@ mod tests {
#[tokio::test]
async fn test_timeline_truncate_long_names() {
- let (agentfs, path, _file) = create_test_agentfs().await;
+ let (vfs, path, _file) = create_test_vfs().await;
// Create a tool call with a very long name (>20 chars)
- agentfs
- .tools
+ vfs.tools
.start("very_long_tool_name_that_exceeds_twenty_characters", None)
.await
.unwrap();
diff --git a/crates/agentfs-cli/src/config.rs b/crates/vfs-cli/src/config.rs
similarity index 93%
rename from crates/agentfs-cli/src/config.rs
rename to crates/vfs-cli/src/config.rs
index f8195a0b..5c03f08f 100644
--- a/crates/agentfs-cli/src/config.rs
+++ b/crates/vfs-cli/src/config.rs
@@ -1,16 +1,16 @@
//! CLI-owned runtime config assembly.
-use agentfs_core::{CoreConfig, EnvReader};
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::sync::{Mutex, OnceLock};
+use vfs_core::{CoreConfig, EnvReader};
-const CLONE_TIMINGS_ENV: &str = "AGENTFS_CLONE_TIMINGS";
+const CLONE_TIMINGS_ENV: &str = "VFS_CLONE_TIMINGS";
const SHELL_ENV: &str = "SHELL";
const TURSO_AUTH_TOKEN_ENV: &str = "TURSO_DB_AUTH_TOKEN";
#[cfg(target_os = "linux")]
-const FUSE_WRITEBACK_ENV: &str = "AGENTFS_FUSE_WRITEBACK";
+const FUSE_WRITEBACK_ENV: &str = "VFS_FUSE_WRITEBACK";
pub(crate) const DEFAULT_CLONE_TIMINGS_ENABLED: bool = false;
@@ -49,7 +49,7 @@ pub(crate) fn turso_db_auth_token() -> Option {
// of `run`/`exec` must NOT inherit that override: the snapshot below is
// restored into their environment by the spawn paths.
-const SPILL_DIR_PREFIX: &str = "agentfs-spill-";
+const SPILL_DIR_PREFIX: &str = "vfs-spill-";
struct SpillDir {
owner_pid: u32,
@@ -175,7 +175,7 @@ fn reap_stale_spill_dirs(base: &Path) {
}
}
-/// Parse the owner PID out of a `agentfs-spill--` directory name.
+/// Parse the owner PID out of a `vfs-spill--` directory name.
fn stale_spill_dir_pid(name: &str) -> Option {
let rest = name.strip_prefix(SPILL_DIR_PREFIX)?;
let (pid, suffix) = rest.split_once('-')?;
@@ -266,12 +266,12 @@ mod tests {
#[test]
fn stale_spill_dir_pid_parses_only_generated_names() {
- assert_eq!(stale_spill_dir_pid("agentfs-spill-1234-0"), Some(1234));
- assert_eq!(stale_spill_dir_pid("agentfs-spill-1234-15"), Some(1234));
- assert_eq!(stale_spill_dir_pid("agentfs-spill-1234"), None);
- assert_eq!(stale_spill_dir_pid("agentfs-spill-abc-0"), None);
- assert_eq!(stale_spill_dir_pid("agentfs-spill-1234-x"), None);
- assert_eq!(stale_spill_dir_pid("agentfs-spill--0"), None);
+ assert_eq!(stale_spill_dir_pid("vfs-spill-1234-0"), Some(1234));
+ assert_eq!(stale_spill_dir_pid("vfs-spill-1234-15"), Some(1234));
+ assert_eq!(stale_spill_dir_pid("vfs-spill-1234"), None);
+ assert_eq!(stale_spill_dir_pid("vfs-spill-abc-0"), None);
+ assert_eq!(stale_spill_dir_pid("vfs-spill-1234-x"), None);
+ assert_eq!(stale_spill_dir_pid("vfs-spill--0"), None);
assert_eq!(stale_spill_dir_pid("tursodb-ephemeral-1"), None);
}
@@ -283,7 +283,7 @@ mod tests {
let alive = base
.path()
.join(format!("{SPILL_DIR_PREFIX}{}-0", std::process::id()));
- let unrelated = base.path().join("agentfs-spill-notes");
+ let unrelated = base.path().join("vfs-spill-notes");
std::fs::create_dir(&dead).unwrap();
std::fs::create_dir(&alive).unwrap();
std::fs::create_dir(&unrelated).unwrap();
diff --git a/crates/agentfs-cli/src/docs.rs b/crates/vfs-cli/src/docs.rs
similarity index 94%
rename from crates/agentfs-cli/src/docs.rs
rename to crates/vfs-cli/src/docs.rs
index 26fe914b..b859bfe0 100644
--- a/crates/agentfs-cli/src/docs.rs
+++ b/crates/vfs-cli/src/docs.rs
@@ -1,18 +1,18 @@
//! Generated clap <-> MANUAL command-reference parity.
//!
//! The command/option sections of docs/MANUAL.md are rendered from the clap
-//! command tree in `opts`, so the manual cannot drift from `agentfs --help`.
+//! command tree in `opts`, so the manual cannot drift from `vfs --help`.
//! The checked-in manual is generated from the Linux CLI surface (the
//! first-tier platform); `docs::tests::manual_help_parity` fails when the
-//! generated region is stale and rewrites it under `AGENTFS_UPDATE_MANUAL=1`.
+//! generated region is stale and rewrites it under `VFS_UPDATE_MANUAL=1`.
use clap::{ArgAction, CommandFactory};
use crate::opts::Args;
#[cfg(all(test, target_os = "linux"))]
-const MANUAL_REGEN_ENV: &str = "AGENTFS_UPDATE_MANUAL";
-const MANUAL_REGEN_COMMAND: &str = "AGENTFS_UPDATE_MANUAL=1 cargo +nightly test -p agentfs-cli --lib docs::tests::manual_help_parity -- --exact";
+const MANUAL_REGEN_ENV: &str = "VFS_UPDATE_MANUAL";
+const MANUAL_REGEN_COMMAND: &str = "VFS_UPDATE_MANUAL=1 cargo +nightly test -p vfs-cli --lib docs::tests::manual_help_parity -- --exact";
const GENERATED_BEGIN: &str = "";
const GENERATED_END: &str = "";
@@ -29,7 +29,7 @@ fn generated_manual_commands() -> String {
out.push_str("## Commands\n\n");
out.push_str(
"Every section below is generated from the clap definitions the binary \
- actually parses; `agentfs --help` and this reference cannot \
+ actually parses; `vfs --help` and this reference cannot \
disagree.\n",
);
for sub in visible_subcommands(&cmd) {
@@ -56,8 +56,8 @@ struct CommandContext {
impl CommandContext {
fn root() -> Self {
Self {
- path: "agentfs".to_string(),
- invocation: "agentfs".to_string(),
+ path: "vfs".to_string(),
+ invocation: "vfs".to_string(),
}
}
@@ -305,7 +305,7 @@ mod tests {
let generated = generated_manual_commands();
let cmd = Args::command();
let mut missing = Vec::new();
- collect_paths(&cmd, "agentfs", &mut |path| {
+ collect_paths(&cmd, "vfs", &mut |path| {
let heading = format!(" {path}\n");
if !generated.contains(&heading) {
missing.push(path.to_string());
@@ -330,7 +330,7 @@ mod tests {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(Path::parent)
- .expect("agentfs-cli crate should live two levels below repo root")
+ .expect("vfs-cli crate should live two levels below repo root")
.to_path_buf()
}
}
diff --git a/crates/agentfs-cli/src/knobs.rs b/crates/vfs-cli/src/knobs.rs
similarity index 87%
rename from crates/agentfs-cli/src/knobs.rs
rename to crates/vfs-cli/src/knobs.rs
index 2bc4ba04..3b8fbdb2 100644
--- a/crates/agentfs-cli/src/knobs.rs
+++ b/crates/vfs-cli/src/knobs.rs
@@ -4,19 +4,19 @@
//! core config, the typed FUSE adapter config, CLI env-backed options, and the
//! first-class partial-origin CLI policy flags.
-use agentfs_core::{
+use vfs_core::{
CoreConfig, DEFAULT_PARTIAL_ORIGIN_THRESHOLD_BYTES, DEFAULT_WRITE_BATCH_BYTES,
DEFAULT_WRITE_BATCH_GLOBAL_BYTES, DEFAULT_WRITE_BATCH_MS, DEFAULT_WRITE_BATCH_TXN_BYTES,
DEFAULT_WRITE_BATCH_TXN_INODES,
};
-use agentfs_core::telemetry::DEFAULT_PROFILE_ENABLED;
+use vfs_core::telemetry::DEFAULT_PROFILE_ENABLED;
use crate::config::DEFAULT_CLONE_TIMINGS_ENABLED;
-// The runtime FUSE config lives in the sealed agentfs-fuse crate while the
+// The runtime FUSE config lives in the sealed vfs-fuse crate while the
// knob ledger lives at the CLI edge. Keep these defaults byte-for-byte aligned
-// with agentfs_fuse::adapter::config; the scan roots below cover every
+// with vfs_fuse::adapter::config; the scan roots below cover every
// workspace crate so undeclared runtime env reads cannot bypass the ledger.
#[cfg(target_os = "linux")]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
@@ -91,6 +91,8 @@ const DEFAULT_URING_DEPTH: usize = 4;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum KnobClass {
ProductConfig,
+ /// Constructed only by Linux FUSE kill-switch knobs today.
+ #[cfg_attr(not(target_os = "linux"), allow(dead_code))]
KillSwitch,
Sunset,
}
@@ -249,11 +251,11 @@ fn render_bool(value: bool) -> &'static str {
}
}
-fn partial_origin_mode_as_str(mode: agentfs_core::PartialOriginMode) -> &'static str {
+fn partial_origin_mode_as_str(mode: vfs_core::PartialOriginMode) -> &'static str {
match mode {
- agentfs_core::PartialOriginMode::Off => "off",
- agentfs_core::PartialOriginMode::On => "on",
- agentfs_core::PartialOriginMode::Auto => "auto",
+ vfs_core::PartialOriginMode::Off => "off",
+ vfs_core::PartialOriginMode::On => "on",
+ vfs_core::PartialOriginMode::Auto => "auto",
}
}
@@ -334,18 +336,18 @@ impl Knob {
}
}
-const CORE_OWNER: &str = "agentfs-core config";
+const CORE_OWNER: &str = "vfs-core config";
#[cfg(target_os = "linux")]
-const FUSE_OWNER: &str = "agentfs FUSE config";
-const CLI_OWNER: &str = "agentfs CLI edge";
+const FUSE_OWNER: &str = "vfs FUSE config";
+const CLI_OWNER: &str = "vfs CLI edge";
#[cfg(all(test, target_os = "linux"))]
-const KNOBS_DOC_REGEN_ENV: &str = "AGENTFS_UPDATE_KNOBS";
-const KNOBS_DOC_REGEN_COMMAND: &str = "AGENTFS_UPDATE_KNOBS=1 cargo +nightly test -p agentfs-cli --lib knobs::tests::generated_knobs_doc_matches_declarations -- --exact";
+const KNOBS_DOC_REGEN_ENV: &str = "VFS_UPDATE_KNOBS";
+const KNOBS_DOC_REGEN_COMMAND: &str = "VFS_UPDATE_KNOBS=1 cargo +nightly test -p vfs-cli --lib knobs::tests::generated_knobs_doc_matches_declarations -- --exact";
#[cfg(target_os = "linux")]
const LINUX_FUSE_KNOBS: &[Knob] = &[
Knob::product(
- "AGENTFS_FUSE_WORKERS",
+ "VFS_FUSE_WORKERS",
"env",
DefaultValue::FuseWorkers,
FUSE_OWNER,
@@ -353,23 +355,23 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"fuse_config::tests::workers_config_feeds_dispatch_and_cache_policy",
),
Knob::product(
- "AGENTFS_FUSE_CPU_PERCENT",
+ "VFS_FUSE_CPU_PERCENT",
"env",
DefaultValue::FuseAutoPercent,
FUSE_OWNER,
- "CPU budget percentage used when AGENTFS_FUSE_WORKERS=auto.",
+ "CPU budget percentage used when VFS_FUSE_WORKERS=auto.",
"knobs::tests::knob_defaults_in_docs_match_runtime_defaults",
),
Knob::product(
- "AGENTFS_FUSE_MEMORY_PERCENT",
+ "VFS_FUSE_MEMORY_PERCENT",
"env",
DefaultValue::FuseAutoPercent,
FUSE_OWNER,
- "Memory budget percentage used when AGENTFS_FUSE_WORKERS=auto.",
+ "Memory budget percentage used when VFS_FUSE_WORKERS=auto.",
"knobs::tests::knob_defaults_in_docs_match_runtime_defaults",
),
Knob::product(
- "AGENTFS_FUSE_QUEUE",
+ "VFS_FUSE_QUEUE",
"env",
DefaultValue::FuseQueue,
FUSE_OWNER,
@@ -377,7 +379,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"fuse_config::tests::workers_config_feeds_dispatch_and_cache_policy",
),
Knob::product(
- "AGENTFS_FUSE_QUEUE_MEMORY_PERCENT",
+ "VFS_FUSE_QUEUE_MEMORY_PERCENT",
"env",
DefaultValue::FuseQueueMemoryPercent,
FUSE_OWNER,
@@ -385,7 +387,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"knobs::tests::knob_defaults_in_docs_match_runtime_defaults",
),
Knob::product(
- "AGENTFS_FUSE_ENTRY_TTL_MS",
+ "VFS_FUSE_ENTRY_TTL_MS",
"env",
DefaultValue::FusePositiveTtlMs,
FUSE_OWNER,
@@ -393,7 +395,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"knobs::tests::knob_defaults_in_docs_match_runtime_defaults",
),
Knob::product(
- "AGENTFS_FUSE_ATTR_TTL_MS",
+ "VFS_FUSE_ATTR_TTL_MS",
"env",
DefaultValue::FusePositiveTtlMs,
FUSE_OWNER,
@@ -401,7 +403,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"knobs::tests::knob_defaults_in_docs_match_runtime_defaults",
),
Knob::product(
- "AGENTFS_FUSE_NEG_TTL_MS",
+ "VFS_FUSE_NEG_TTL_MS",
"env",
DefaultValue::FuseNegativeTtlMs,
FUSE_OWNER,
@@ -409,7 +411,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"knobs::tests::knob_defaults_in_docs_match_runtime_defaults",
),
Knob::product(
- "AGENTFS_FUSE_WRITEBACK",
+ "VFS_FUSE_WRITEBACK",
"env",
DefaultValue::FuseWriteback,
FUSE_OWNER,
@@ -417,7 +419,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"fuse_config::tests::serial_dispatch_disables_kernel_cache_policy",
),
Knob::product(
- "AGENTFS_FUSE_KEEPCACHE",
+ "VFS_FUSE_KEEPCACHE",
"env",
DefaultValue::FuseKeepcache,
FUSE_OWNER,
@@ -425,7 +427,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"fuse_config::tests::serial_dispatch_disables_kernel_cache_policy",
),
Knob::product(
- "AGENTFS_FUSE_READDIRPLUS",
+ "VFS_FUSE_READDIRPLUS",
"env",
DefaultValue::FuseReaddirPlus,
FUSE_OWNER,
@@ -433,7 +435,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"fuse_config::tests::invalid_readdirplus_warns_and_defaults_on",
),
Knob::product(
- "AGENTFS_FUSE_SYNC_INVAL",
+ "VFS_FUSE_SYNC_INVAL",
"env",
DefaultValue::FuseSyncInval,
FUSE_OWNER,
@@ -441,7 +443,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"fuse_config::tests::serial_dispatch_disables_kernel_cache_policy",
),
Knob::sunset(
- "AGENTFS_FUSE_SELF_INVAL",
+ "VFS_FUSE_SELF_INVAL",
"env",
DefaultValue::FuseSelfInval,
FUSE_OWNER,
@@ -450,7 +452,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"VAL-FUSE-011 and VAL-FUSE-018",
),
Knob::sunset(
- "AGENTFS_DRAIN_ON_RELEASE",
+ "VFS_DRAIN_ON_RELEASE",
"env",
DefaultValue::FuseDrainOnRelease,
FUSE_OWNER,
@@ -459,7 +461,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"VAL-FUSE-015",
),
Knob::sunset(
- "AGENTFS_DRAIN_ON_FORGET",
+ "VFS_DRAIN_ON_FORGET",
"env",
DefaultValue::FuseDrainOnForget,
FUSE_OWNER,
@@ -468,7 +470,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"VAL-FUSE-003 and VAL-FUSE-004",
),
Knob::sunset(
- "AGENTFS_FUSE_FLUSH_INVAL",
+ "VFS_FUSE_FLUSH_INVAL",
"env",
DefaultValue::FuseFlushInval,
FUSE_OWNER,
@@ -477,21 +479,21 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"VAL-FUSE-007 and VAL-FUSE-008",
),
Knob::kill_switch(
- "AGENTFS_FUSE_NOFLUSH",
+ "VFS_FUSE_NOFLUSH",
DefaultValue::FuseNoflush,
FUSE_OWNER,
"Disables close-time FLUSH by returning ENOSYS after the kernel has written dirty pages.",
"VAL-FUSE-007 default leg and VAL-FUSE-008 off leg",
),
Knob::kill_switch(
- "AGENTFS_FUSE_NOOPEN",
+ "VFS_FUSE_NOOPEN",
DefaultValue::FuseNoopen,
FUSE_OWNER,
"Disables per-file OPEN/RELEASE by returning ENOSYS when the kernel supports no-open.",
"VAL-FUSE-003 default leg and VAL-FUSE-004 off leg",
),
Knob::product(
- "AGENTFS_FUSE_INO_FILES_CAP",
+ "VFS_FUSE_INO_FILES_CAP",
"env",
DefaultValue::FuseInoFilesCap,
FUSE_OWNER,
@@ -499,7 +501,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"scripts/validation/noopen-coherence.py",
),
Knob::product(
- "AGENTFS_FUSE_CACHE_DIR",
+ "VFS_FUSE_CACHE_DIR",
"env",
DefaultValue::FuseCacheDir,
FUSE_OWNER,
@@ -507,7 +509,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"VAL-FUSE-011 and VAL-FUSE-018",
),
Knob::sunset(
- "AGENTFS_FUSE_STICKY_KEEPCACHE_DROP",
+ "VFS_FUSE_STICKY_KEEPCACHE_DROP",
"env",
DefaultValue::FuseStickyKeepcacheDrop,
FUSE_OWNER,
@@ -516,14 +518,14 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"VAL-FUSE-012 and VAL-FUSE-014",
),
Knob::kill_switch(
- "AGENTFS_FUSE_URING",
+ "VFS_FUSE_URING",
DefaultValue::FuseUringEnabled,
FUSE_OWNER,
"FUSE-over-io_uring transport attempt. Set false to force the legacy /dev/fuse path.",
"VAL-FUSE-009 uring leg and VAL-FUSE-010 off leg",
),
Knob::product(
- "AGENTFS_FUSE_URING_DEPTH",
+ "VFS_FUSE_URING_DEPTH",
"env",
DefaultValue::FuseUringDepth,
FUSE_OWNER,
@@ -531,7 +533,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[
"VAL-FUSE-009",
),
Knob::sunset(
- "AGENTFS_FUSE_URING_SPIN_US",
+ "VFS_FUSE_URING_SPIN_US",
"env",
DefaultValue::FuseUringSpinUs,
FUSE_OWNER,
@@ -546,7 +548,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[];
const ACTIVE_COMMON_KNOBS: &[Knob] = &[
Knob::product(
- "AGENTFS_KEY",
+ "VFS_KEY",
"env or --key",
DefaultValue::Unset,
CLI_OWNER,
@@ -554,15 +556,15 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
"opts clap env binding",
),
Knob::product(
- "AGENTFS_CIPHER",
+ "VFS_CIPHER",
"env or --cipher",
DefaultValue::Unset,
CLI_OWNER,
- "Encryption cipher paired with AGENTFS_KEY or --key.",
+ "Encryption cipher paired with VFS_KEY or --key.",
"opts clap env binding",
),
Knob::sunset(
- "AGENTFS_CLONE_TIMINGS",
+ "VFS_CLONE_TIMINGS",
"env",
DefaultValue::CloneTimingsEnabled,
CLI_OWNER,
@@ -571,7 +573,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
"config::clone_timings_enabled",
),
Knob::product(
- "AGENTFS_PROFILE",
+ "VFS_PROFILE",
"env",
DefaultValue::ProfileEnabled,
CORE_OWNER,
@@ -579,7 +581,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
"VAL-CONF-011 and VAL-CONF-014",
),
Knob::sunset(
- "AGENTFS_OVERLAY_READS",
+ "VFS_OVERLAY_READS",
"env",
DefaultValue::CoreOverlayReads,
CORE_OWNER,
@@ -588,7 +590,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
"VAL-CORE-006 and phase8 smoke",
),
Knob::product(
- "AGENTFS_KEEPCACHE_DELTA",
+ "VFS_KEEPCACHE_DELTA",
"env",
DefaultValue::CoreKeepcacheDelta,
CORE_OWNER,
@@ -596,7 +598,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
"VAL-FUSE-014",
),
Knob::product(
- "AGENTFS_BATCH_MS",
+ "VFS_BATCH_MS",
"env",
DefaultValue::WriteBatchMs,
CORE_OWNER,
@@ -604,7 +606,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
"sdk write-batcher tests",
),
Knob::product(
- "AGENTFS_BATCH_BYTES",
+ "VFS_BATCH_BYTES",
"env",
DefaultValue::WriteBatchBytes,
CORE_OWNER,
@@ -612,7 +614,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
"sdk write-batcher tests",
),
Knob::product(
- "AGENTFS_BATCH_GLOBAL_BYTES",
+ "VFS_BATCH_GLOBAL_BYTES",
"env",
DefaultValue::WriteBatchGlobalBytes,
CORE_OWNER,
@@ -620,7 +622,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
"sdk write-batcher tests",
),
Knob::product(
- "AGENTFS_BATCH_TXN_INODES",
+ "VFS_BATCH_TXN_INODES",
"env",
DefaultValue::WriteBatchTxnInodes,
CORE_OWNER,
@@ -628,7 +630,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
"sdk write-batcher and import tests",
),
Knob::product(
- "AGENTFS_BATCH_TXN_BYTES",
+ "VFS_BATCH_TXN_BYTES",
"env",
DefaultValue::WriteBatchTxnBytes,
CORE_OWNER,
@@ -636,7 +638,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
"sdk write-batcher and import tests",
),
Knob::sunset(
- "AGENTFS_DRAIN_ON_SETATTR",
+ "VFS_DRAIN_ON_SETATTR",
"env",
DefaultValue::CoreDrainOnSetattr,
CORE_OWNER,
@@ -663,7 +665,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[
];
const DELETED_COMPAT_KNOBS: &[Knob] = &[Knob::sunset(
- concat!("AGENTFS_OVERLAY_", "PARTIAL_ORIGIN"),
+ concat!("VFS_OVERLAY_", "PARTIAL_ORIGIN"),
"deleted env compat",
DefaultValue::Removed,
CORE_OWNER,
@@ -689,8 +691,8 @@ fn deleted_compat_knobs() -> &'static [Knob] {
fn generated_knobs_doc() -> String {
let active = active_knobs();
let mut out = String::new();
- out.push_str("# AgentFS Runtime Knobs\n\n");
- out.push_str("\n\n");
out.push_str(
@@ -791,19 +793,19 @@ mod tests {
fn knob_defaults_in_docs_match_runtime_defaults() {
let active = active_knobs();
let expected = [
- ("AGENTFS_FUSE_WORKERS", "auto"),
- ("AGENTFS_FUSE_QUEUE", "derived"),
- ("AGENTFS_FUSE_ENTRY_TTL_MS", "10000"),
- ("AGENTFS_FUSE_ATTR_TTL_MS", "10000"),
- ("AGENTFS_FUSE_CPU_PERCENT", "50"),
- ("AGENTFS_FUSE_MEMORY_PERCENT", "50"),
- ("AGENTFS_FUSE_QUEUE_MEMORY_PERCENT", "25"),
- ("AGENTFS_BATCH_MS", "5"),
- ("AGENTFS_BATCH_BYTES", "4194304"),
- ("AGENTFS_BATCH_GLOBAL_BYTES", "67108864"),
- ("AGENTFS_BATCH_TXN_INODES", "1024"),
- ("AGENTFS_BATCH_TXN_BYTES", "33554432"),
- ("AGENTFS_PROFILE", "false"),
+ ("VFS_FUSE_WORKERS", "auto"),
+ ("VFS_FUSE_QUEUE", "derived"),
+ ("VFS_FUSE_ENTRY_TTL_MS", "10000"),
+ ("VFS_FUSE_ATTR_TTL_MS", "10000"),
+ ("VFS_FUSE_CPU_PERCENT", "50"),
+ ("VFS_FUSE_MEMORY_PERCENT", "50"),
+ ("VFS_FUSE_QUEUE_MEMORY_PERCENT", "25"),
+ ("VFS_BATCH_MS", "5"),
+ ("VFS_BATCH_BYTES", "4194304"),
+ ("VFS_BATCH_GLOBAL_BYTES", "67108864"),
+ ("VFS_BATCH_TXN_INODES", "1024"),
+ ("VFS_BATCH_TXN_BYTES", "33554432"),
+ ("VFS_PROFILE", "false"),
];
for (name, default) in expected {
@@ -839,15 +841,15 @@ mod tests {
sunset_names.sort_unstable();
println!("sunset knobs: {}", sunset_names.join(", "));
for required in [
- "AGENTFS_CLONE_TIMINGS",
- "AGENTFS_FUSE_SELF_INVAL",
- "AGENTFS_DRAIN_ON_RELEASE",
- "AGENTFS_DRAIN_ON_FORGET",
- "AGENTFS_FUSE_FLUSH_INVAL",
- "AGENTFS_FUSE_STICKY_KEEPCACHE_DROP",
- "AGENTFS_FUSE_URING_SPIN_US",
- "AGENTFS_DRAIN_ON_SETATTR",
- "AGENTFS_OVERLAY_READS",
+ "VFS_CLONE_TIMINGS",
+ "VFS_FUSE_SELF_INVAL",
+ "VFS_DRAIN_ON_RELEASE",
+ "VFS_DRAIN_ON_FORGET",
+ "VFS_FUSE_FLUSH_INVAL",
+ "VFS_FUSE_STICKY_KEEPCACHE_DROP",
+ "VFS_FUSE_URING_SPIN_US",
+ "VFS_DRAIN_ON_SETATTR",
+ "VFS_OVERLAY_READS",
] {
assert!(
sunset_names.contains(&required),
@@ -860,11 +862,7 @@ mod tests {
#[test]
fn fuse_kill_switches_are_declared_and_gated() {
let active = active_knobs();
- for name in [
- "AGENTFS_FUSE_NOOPEN",
- "AGENTFS_FUSE_NOFLUSH",
- "AGENTFS_FUSE_URING",
- ] {
+ for name in ["VFS_FUSE_NOOPEN", "VFS_FUSE_NOFLUSH", "VFS_FUSE_URING"] {
let knob = find_knob(&active, name);
assert_eq!(knob.class, KnobClass::KillSwitch, "{name} class drifted");
assert!(
@@ -931,11 +929,11 @@ mod tests {
let root = repo_root();
let mut tokens = BTreeSet::new();
for source_root in [
- root.join("crates/agentfs-cli/src"),
- root.join("crates/agentfs-core/src"),
- root.join("crates/agentfs-fuse/src"),
- root.join("crates/agentfs-mount/src"),
- root.join("crates/agentfs-nfs/src"),
+ root.join("crates/vfs-cli/src"),
+ root.join("crates/vfs-core/src"),
+ root.join("crates/vfs-fuse/src"),
+ root.join("crates/vfs-mount/src"),
+ root.join("crates/vfs-nfs/src"),
] {
collect_runtime_env_mentions_from_path(&source_root, &mut tokens);
}
@@ -973,7 +971,7 @@ mod tests {
#[cfg(target_os = "linux")]
fn extract_env_tokens(contents: &str) -> BTreeSet {
let mut tokens = BTreeSet::new();
- for prefix in ["AGENTFS_", "TURSO_DB_AUTH_TOKEN"] {
+ for prefix in ["VFS_", "TURSO_DB_AUTH_TOKEN"] {
let mut offset = 0;
while let Some(index) = contents[offset..].find(prefix) {
let start = offset + index;
@@ -996,15 +994,15 @@ mod tests {
fn ignored_env_token(name: &str) -> bool {
matches!(
name,
- "AGENTFS_VERSION"
- | "AGENTFS_SCHEMA_VERSION"
- | "AGENTFS_SANDBOX"
- | "AGENTFS_SESSION"
+ "VFS_VERSION"
+ | "VFS_SCHEMA_VERSION"
+ | "VFS_SANDBOX"
+ | "VFS_SESSION"
| "TURSO_DB_AUTH_TOKEN"
// Doc-regeneration test flag (docs.rs), not a runtime knob;
// its knobs.rs sibling is exempt via the knobs.rs file skip.
- | "AGENTFS_UPDATE_MANUAL"
- ) || name.starts_with("AGENTFS_TEST_")
+ | "VFS_UPDATE_MANUAL"
+ ) || name.starts_with("VFS_TEST_")
|| name.ends_with('_')
}
@@ -1012,7 +1010,7 @@ mod tests {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(Path::parent)
- .expect("agentfs-cli crate should live two levels below repo root")
+ .expect("vfs-cli crate should live two levels below repo root")
.to_path_buf()
}
}
diff --git a/crates/agentfs-cli/src/lib.rs b/crates/vfs-cli/src/lib.rs
similarity index 86%
rename from crates/agentfs-cli/src/lib.rs
rename to crates/vfs-cli/src/lib.rs
index 2b874aa1..0ea99027 100644
--- a/crates/agentfs-cli/src/lib.rs
+++ b/crates/vfs-cli/src/lib.rs
@@ -1,9 +1,9 @@
-//! Thin CLI edge for AgentFS.
+//! Thin CLI edge for Vfs.
//!
//! The CLI crate owns argument parsing, user-facing command output, process
//! exit/reporting behavior, and runtime config assembly. Filesystem semantics,
-//! transport adapters, and mount lifecycle code live in `agentfs-core`,
-//! `agentfs-fuse`, `agentfs-nfs`, and `agentfs-mount` respectively.
+//! transport adapters, and mount lifecycle code live in `vfs-core`,
+//! `vfs-fuse`, `vfs-nfs`, and `vfs-mount` respectively.
//!
//! Owned invariants:
//!
diff --git a/crates/agentfs-cli/src/logging.rs b/crates/vfs-cli/src/logging.rs
similarity index 67%
rename from crates/agentfs-cli/src/logging.rs
rename to crates/vfs-cli/src/logging.rs
index a09f9551..d193308e 100644
--- a/crates/agentfs-cli/src/logging.rs
+++ b/crates/vfs-cli/src/logging.rs
@@ -1,16 +1,16 @@
//! CLI logging defaults.
//!
//! The binary installs the tracing subscriber, but the default filter must
-//! cover every first-party AgentFS crate so warnings are visible even when
+//! cover every first-party Vfs crate so warnings are visible even when
//! `RUST_LOG` is unset.
const DEFAULT_ENV_FILTER: &str = concat!(
- "agentfs=info,",
- "agentfs_cli=info,",
- "agentfs_core=info,",
- "agentfs_fuse=info,",
- "agentfs_nfs=info,",
- "agentfs_mount=info"
+ "vfs=info,",
+ "vfs_cli=info,",
+ "vfs_core=info,",
+ "vfs_fuse=info,",
+ "vfs_nfs=info,",
+ "vfs_mount=info"
);
pub fn default_env_filter() -> tracing_subscriber::EnvFilter {
@@ -47,28 +47,22 @@ mod tests {
}
#[test]
- fn default_env_filter_covers_all_agentfs_crates() {
+ fn default_env_filter_covers_all_vfs_crates() {
let previous = std::env::var_os("RUST_LOG");
std::env::remove_var("RUST_LOG");
let seen = SeenTargets::default();
- let targets = [
- "agentfs_core",
- "agentfs_fuse",
- "agentfs_nfs",
- "agentfs_mount",
- "agentfs_cli",
- ];
+ let targets = ["vfs_core", "vfs_fuse", "vfs_nfs", "vfs_mount", "vfs_cli"];
let subscriber = Registry::default()
.with(default_env_filter())
.with(seen.clone());
tracing::dispatcher::with_default(&subscriber.into(), || {
- event!(target: "agentfs_core", Level::WARN, "default filter coverage probe");
- event!(target: "agentfs_fuse", Level::WARN, "default filter coverage probe");
- event!(target: "agentfs_nfs", Level::WARN, "default filter coverage probe");
- event!(target: "agentfs_mount", Level::WARN, "default filter coverage probe");
- event!(target: "agentfs_cli", Level::WARN, "default filter coverage probe");
+ event!(target: "vfs_core", Level::WARN, "default filter coverage probe");
+ event!(target: "vfs_fuse", Level::WARN, "default filter coverage probe");
+ event!(target: "vfs_nfs", Level::WARN, "default filter coverage probe");
+ event!(target: "vfs_mount", Level::WARN, "default filter coverage probe");
+ event!(target: "vfs_cli", Level::WARN, "default filter coverage probe");
});
match previous {
diff --git a/crates/agentfs-cli/src/main.rs b/crates/vfs-cli/src/main.rs
similarity index 93%
rename from crates/agentfs-cli/src/main.rs
rename to crates/vfs-cli/src/main.rs
index 370c7869..e2b2b85a 100644
--- a/crates/agentfs-cli/src/main.rs
+++ b/crates/vfs-cli/src/main.rs
@@ -1,12 +1,12 @@
-use agentfs_cli::{
+use clap::{CommandFactory, Parser};
+use clap_complete::CompleteEnv;
+use tracing_subscriber::prelude::*;
+use vfs_cli::{
cmd::{self, completions::handle_completions},
get_runtime,
logging::default_env_filter,
opts::{Args, Command, FsCommand, PruneCommand, ServeCommand, SyncCommand},
};
-use clap::{CommandFactory, Parser};
-use clap_complete::CompleteEnv;
-use tracing_subscriber::prelude::*;
/// Parse and validate encryption key and cipher options.
/// Both must be provided together or neither.
@@ -23,20 +23,20 @@ fn parse_encryption(
}
fn partial_origin_policy(
- mode: Option,
+ mode: Option,
threshold_bytes: Option,
-) -> Option {
+) -> Option {
match (mode, threshold_bytes) {
(None, None) => None,
(Some(mode), threshold_bytes) => {
- let mut policy = agentfs_core::PartialOriginPolicy::new(mode.into());
+ let mut policy = vfs_core::PartialOriginPolicy::new(mode.into());
if let Some(threshold_bytes) = threshold_bytes {
policy = policy.with_threshold_bytes(threshold_bytes);
}
Some(policy)
}
(None, Some(threshold_bytes)) => Some(
- agentfs_core::PartialOriginPolicy::new(agentfs_core::PartialOriginMode::Auto)
+ vfs_core::PartialOriginPolicy::new(vfs_core::PartialOriginMode::Auto)
.with_threshold_bytes(threshold_bytes),
),
}
@@ -48,14 +48,14 @@ fn exit_with_error(message: impl std::fmt::Display) -> ! {
}
fn exit_with_code(code: i32) -> ! {
- agentfs_cli::profiling::emit_cli_report();
+ vfs_cli::profiling::emit_cli_report();
std::process::exit(code);
}
fn main() {
// Before any threads exist: the TMPDIR override mutates the process
// environment (see config::init_private_spill_dir).
- agentfs_cli::config::init_private_spill_dir();
+ vfs_cli::config::init_private_spill_dir();
let _ = tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer())
@@ -65,7 +65,7 @@ fn main() {
reset_sigpipe();
CompleteEnv::with_factory(Args::command).complete();
- let _profile_report = agentfs_cli::profiling::install_cli_sink();
+ let _profile_report = vfs_cli::profiling::install_cli_sink();
let args = match Args::try_parse() {
Ok(args) => args,
Err(error) => {
@@ -137,8 +137,8 @@ fn dispatch(args: Args) -> anyhow::Result<()> {
args,
} => {
let encryption = parse_encryption(key, cipher)?
- .map(|(hex_key, cipher)| agentfs_core::EncryptionConfig { hex_key, cipher });
- let options = agentfs_cli::opts::RunOptions {
+ .map(|(hex_key, cipher)| vfs_core::EncryptionConfig { hex_key, cipher });
+ let options = vfs_cli::opts::RunOptions {
allow,
no_default_allows,
session,
@@ -281,7 +281,7 @@ fn dispatch(args: Args) -> anyhow::Result<()> {
key,
cipher,
} => {
- eprintln!("Warning: `agentfs nfs` is deprecated, use `agentfs serve nfs` instead");
+ eprintln!("Warning: `vfs nfs` is deprecated, use `vfs serve nfs` instead");
let encryption = parse_encryption(key, cipher)?;
let rt = get_runtime();
rt.block_on(cmd::nfs::handle_nfs_command(
@@ -289,9 +289,7 @@ fn dispatch(args: Args) -> anyhow::Result<()> {
))
}
Command::McpServer { id_or_path, tools } => {
- eprintln!(
- "Warning: `agentfs mcp-server` is deprecated, use `agentfs serve mcp` instead"
- );
+ eprintln!("Warning: `vfs mcp-server` is deprecated, use `vfs serve mcp` instead");
let rt = get_runtime();
rt.block_on(cmd::mcp_server::handle_mcp_server_command(
id_or_path, tools,
@@ -440,17 +438,17 @@ fn default_shell() -> std::path::PathBuf {
#[cfg(test)]
mod partial_origin {
use super::partial_origin_policy;
- use agentfs_cli::opts::{Args, Command, PartialOriginMode};
use clap::Parser;
+ use vfs_cli::opts::{Args, Command, PartialOriginMode};
#[test]
fn legacy_env_does_not_override_cli_off() {
- let key = concat!("AGENTFS_OVERLAY_", "PARTIAL_ORIGIN");
+ let key = concat!("VFS_OVERLAY_", "PARTIAL_ORIGIN");
let previous = std::env::var(key).ok();
std::env::set_var(key, "1");
let args = Args::try_parse_from([
- "agentfs",
+ "vfs",
"run",
"--partial-origin",
"off",
@@ -482,6 +480,6 @@ mod partial_origin {
policy.mode
);
assert_eq!(mode, Some(PartialOriginMode::Off));
- assert_eq!(policy.mode, agentfs_core::PartialOriginMode::Off);
+ assert_eq!(policy.mode, vfs_core::PartialOriginMode::Off);
}
}
diff --git a/crates/agentfs-cli/src/opts.rs b/crates/vfs-cli/src/opts.rs
similarity index 90%
rename from crates/agentfs-cli/src/opts.rs
rename to crates/vfs-cli/src/opts.rs
index c5edf2cf..ab7a7305 100644
--- a/crates/agentfs-cli/src/opts.rs
+++ b/crates/vfs-cli/src/opts.rs
@@ -1,10 +1,10 @@
use crate::cmd::completions::Shell;
-use agentfs_core::agentfs_dir;
use clap::{Parser, Subcommand};
use clap_complete::{
engine::ValueCompleter, ArgValueCompleter, CompletionCandidate, PathCompleter,
};
use std::path::{Path, PathBuf};
+use vfs_core::vfs_dir;
/// Mount backend type
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
@@ -39,11 +39,11 @@ impl std::fmt::Display for MountBackend {
}
}
-impl From for agentfs_mount::Backend {
+impl From for vfs_mount::Backend {
fn from(value: MountBackend) -> Self {
match value {
- MountBackend::Fuse => agentfs_mount::Backend::Fuse,
- MountBackend::Nfs => agentfs_mount::Backend::Nfs,
+ MountBackend::Fuse => vfs_mount::Backend::Fuse,
+ MountBackend::Nfs => vfs_mount::Backend::Nfs,
}
}
}
@@ -59,17 +59,17 @@ pub enum PartialOriginMode {
Auto,
}
-impl From for agentfs_core::PartialOriginMode {
+impl From for vfs_core::PartialOriginMode {
fn from(value: PartialOriginMode) -> Self {
match value {
- PartialOriginMode::Off => agentfs_core::PartialOriginMode::Off,
- PartialOriginMode::On => agentfs_core::PartialOriginMode::On,
- PartialOriginMode::Auto => agentfs_core::PartialOriginMode::Auto,
+ PartialOriginMode::Off => vfs_core::PartialOriginMode::Off,
+ PartialOriginMode::On => vfs_core::PartialOriginMode::On,
+ PartialOriginMode::Auto => vfs_core::PartialOriginMode::Auto,
}
}
}
-/// Resolved knobs for `agentfs run`.
+/// Resolved knobs for `vfs run`.
///
/// One struct threaded through `cmd/run/*` so adding a run knob touches the
/// clap arm, this struct, and the platform backend that consumes it — not a
@@ -85,9 +85,9 @@ pub struct RunOptions {
/// Allow other system users to access the mount (FUSE allow_other).
pub system: bool,
/// Delta-layer encryption, already validated at the CLI edge.
- pub encryption: Option,
+ pub encryption: Option,
/// Partial-origin copy-up policy resolved from CLI flags.
- pub partial_origin_policy: Option,
+ pub partial_origin_policy: Option,
/// Command to execute inside the sandbox.
pub command: PathBuf,
/// Arguments for the command.
@@ -95,8 +95,8 @@ pub struct RunOptions {
}
#[derive(Parser, Debug)]
-#[command(name = "agentfs")]
-#[command(version = env!("AGENTFS_VERSION"))]
+#[command(name = "vfs")]
+#[command(version = env!("VFS_VERSION"))]
#[command(about = "The filesystem for agents", long_about = None)]
pub struct Args {
#[command(subcommand)]
@@ -139,12 +139,12 @@ pub enum Command {
/// Hex-encoded encryption key.
/// Enables local encryption when provided.
- #[arg(long, env = "AGENTFS_KEY")]
+ #[arg(long, env = "VFS_KEY")]
key: Option,
/// Cipher algorithm for encryption (required with --key).
/// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm
- #[arg(long, env = "AGENTFS_CIPHER")]
+ #[arg(long, env = "VFS_CIPHER")]
cipher: Option,
/// Command to execute after initialization (mounts the filesystem, runs command, unmounts)
@@ -174,12 +174,12 @@ pub enum Command {
id_or_path: String,
/// Hex-encoded encryption key for encrypted databases.
- #[arg(long, env = "AGENTFS_KEY")]
+ #[arg(long, env = "VFS_KEY")]
key: Option,
/// Cipher algorithm for encryption (required with --key).
/// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm
- #[arg(long, env = "AGENTFS_CIPHER")]
+ #[arg(long, env = "VFS_CIPHER")]
cipher: Option,
#[command(subcommand)]
@@ -189,7 +189,7 @@ pub enum Command {
///
/// By default, uses FUSE+overlay with Linux user and mount namespaces for isolation.
/// The overlay uses the host filesystem as a read-only base and stores
- /// all changes in an AgentFS-backed delta layer. On macOS the overlay is
+ /// all changes in a Vfs-backed delta layer. On macOS the overlay is
/// mounted over NFS and a generated Seatbelt profile scopes writes to the
/// sandbox and reads to the allowed directories plus required platform
/// paths (see the Sandboxing section of docs/MANUAL.md).
@@ -223,12 +223,12 @@ pub enum Command {
/// Hex-encoded encryption key for the delta layer.
/// Enables local encryption when provided.
- #[arg(long, env = "AGENTFS_KEY")]
+ #[arg(long, env = "VFS_KEY")]
key: Option,
/// Cipher algorithm for encryption (required with --key).
/// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm
- #[arg(long, env = "AGENTFS_CIPHER")]
+ #[arg(long, env = "VFS_CIPHER")]
cipher: Option,
/// Command to execute (defaults to bash on Linux, zsh on macOS)
@@ -238,9 +238,9 @@ pub enum Command {
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec,
},
- /// Execute a command with an AgentFS filesystem mounted.
+ /// Execute a command with a Vfs filesystem mounted.
///
- /// Mounts the specified AgentFS to a temporary directory, runs the command
+ /// Mounts the specified Vfs to a temporary directory, runs the command
/// with that directory as the working directory, then automatically unmounts.
/// This is useful for running tools that need filesystem access without
/// a persistent mount.
@@ -263,14 +263,14 @@ pub enum Command {
backend: MountBackend,
/// Hex-encoded encryption key for encrypted databases.
- #[arg(long, env = "AGENTFS_KEY")]
+ #[arg(long, env = "VFS_KEY")]
key: Option,
/// Cipher algorithm for encryption (required with --key).
- #[arg(long, env = "AGENTFS_CIPHER")]
+ #[arg(long, env = "VFS_CIPHER")]
cipher: Option,
},
- /// Clone a git repository into an AgentFS database (fast bulk ingest).
+ /// Clone a git repository into a Vfs database (fast bulk ingest).
///
/// Runs `git clone --no-checkout` through a temporary mount (pack files
/// are large sequential writes), then materializes the worktree by
@@ -350,12 +350,12 @@ pub enum Command {
partial_origin_threshold_bytes: Option,
/// Hex-encoded encryption key for encrypted databases.
- #[arg(long, env = "AGENTFS_KEY")]
+ #[arg(long, env = "VFS_KEY")]
key: Option,
/// Cipher algorithm for encryption (required with --key).
/// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm
- #[arg(long, env = "AGENTFS_CIPHER")]
+ #[arg(long, env = "VFS_CIPHER")]
cipher: Option,
},
/// Show differences between base filesystem and delta (overlay mode only)
@@ -386,8 +386,8 @@ pub enum Command {
#[arg(long, default_value = "table", value_parser = ["table", "json"])]
format: String,
},
- /// Start an NFS server to export an AgentFS filesystem over the network
- /// (deprecated: use `agentfs serve nfs` instead)
+ /// Start an NFS server to export a Vfs filesystem over the network
+ /// (deprecated: use `vfs serve nfs` instead)
#[cfg(unix)]
Nfs {
/// Agent ID or database path
@@ -403,17 +403,17 @@ pub enum Command {
port: u32,
/// Hex-encoded encryption key for encrypted databases.
- #[arg(long, env = "AGENTFS_KEY")]
+ #[arg(long, env = "VFS_KEY")]
key: Option,
/// Cipher algorithm for encryption (required with --key).
/// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm
- #[arg(long, env = "AGENTFS_CIPHER")]
+ #[arg(long, env = "VFS_CIPHER")]
cipher: Option,
},
/// Start an MCP server exposing filesystem and KV-store tools
- /// (deprecated: use `agentfs serve mcp` instead)
+ /// (deprecated: use `vfs serve mcp` instead)
McpServer {
/// Agent ID or database path
#[arg(value_name = "ID_OR_PATH", add = ArgValueCompleter::new(id_or_path_completer))]
@@ -426,19 +426,19 @@ pub enum Command {
tools: Option>,
},
- /// Serve an AgentFS filesystem via different protocols
+ /// Serve a Vfs filesystem via different protocols
Serve {
#[command(subcommand)]
command: ServeCommand,
},
- /// List active agentfs run sessions
+ /// List active vfs run sessions
Ps,
/// Prune unused resources
Prune {
#[command(subcommand)]
command: PruneCommand,
},
- /// Check a local AgentFS database for SQLite and schema corruption
+ /// Check a local Vfs database for SQLite and schema corruption
Integrity {
/// Agent ID or database path
#[arg(add = ArgValueCompleter::new(id_or_path_completer))]
@@ -468,7 +468,7 @@ pub enum Command {
#[arg(long)]
cipher: Option,
},
- /// Create a portable local AgentFS database backup
+ /// Create a portable local Vfs database backup
Backup {
/// Agent ID or database path
#[arg(add = ArgValueCompleter::new(id_or_path_completer))]
@@ -573,9 +573,9 @@ pub enum FsCommand {
#[derive(Subcommand, Debug)]
pub enum SyncCommand {
- /// Pull remote changes (only of agentfs was initialized with remote sync)
+ /// Pull remote changes (only of vfs was initialized with remote sync)
Pull,
- /// Push remote changes (only of agentfs was initialized with remote sync)
+ /// Push remote changes (only of vfs was initialized with remote sync)
Push,
/// Print synced database stats
Stats,
@@ -585,7 +585,7 @@ pub enum SyncCommand {
#[derive(Subcommand, Debug)]
pub enum ServeCommand {
- /// Start an NFS server to export an AgentFS filesystem over the network
+ /// Start an NFS server to export a Vfs filesystem over the network
#[cfg(unix)]
Nfs {
/// Agent ID or database path
@@ -601,12 +601,12 @@ pub enum ServeCommand {
port: u32,
/// Hex-encoded encryption key for encrypted databases.
- #[arg(long, env = "AGENTFS_KEY")]
+ #[arg(long, env = "VFS_KEY")]
key: Option,
/// Cipher algorithm for encryption (required with --key).
/// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm
- #[arg(long, env = "AGENTFS_CIPHER")]
+ #[arg(long, env = "VFS_CIPHER")]
cipher: Option,
},
@@ -626,7 +626,7 @@ pub enum ServeCommand {
#[derive(Subcommand, Debug)]
pub enum PruneCommand {
- /// Unmount unused agentfs mount points
+ /// Unmount unused vfs mount points
Mounts {
/// Skip confirmation prompt and unmount immediately
#[arg(long)]
@@ -658,8 +658,8 @@ fn id_completer(current: &std::ffi::OsStr) -> Vec {
return completions;
};
- let agentfs_dir = agentfs_dir();
- let Ok(read_dir) = agentfs_dir.read_dir() else {
+ let vfs_dir = vfs_dir();
+ let Ok(read_dir) = vfs_dir.read_dir() else {
return completions;
};
@@ -708,7 +708,7 @@ mod tests {
#[test]
fn run_partial_origin_options_parse() {
let args = Args::try_parse_from([
- "agentfs",
+ "vfs",
"run",
"--partial-origin",
"auto",
@@ -736,14 +736,14 @@ mod tests {
#[test]
fn mount_partial_origin_options_parse() {
let args = Args::try_parse_from([
- "agentfs",
+ "vfs",
"mount",
"--partial-origin",
"on",
"--partial-origin-threshold-bytes",
"8192",
"agent",
- "/tmp/agentfs-mnt",
+ "/tmp/vfs-mnt",
])
.unwrap();
@@ -758,7 +758,7 @@ mod tests {
assert_eq!(partial_origin, Some(PartialOriginMode::On));
assert_eq!(partial_origin_threshold_bytes, Some(8192));
assert_eq!(id_or_path.as_deref(), Some("agent"));
- assert_eq!(mountpoint, Some(PathBuf::from("/tmp/agentfs-mnt")));
+ assert_eq!(mountpoint, Some(PathBuf::from("/tmp/vfs-mnt")));
}
other => panic!("expected mount command, got {other:?}"),
}
diff --git a/crates/agentfs-cli/tests/.gitignore b/crates/vfs-cli/tests/.gitignore
similarity index 100%
rename from crates/agentfs-cli/tests/.gitignore
rename to crates/vfs-cli/tests/.gitignore
diff --git a/crates/agentfs-cli/tests/all.sh b/crates/vfs-cli/tests/all.sh
similarity index 78%
rename from crates/agentfs-cli/tests/all.sh
rename to crates/vfs-cli/tests/all.sh
index 5f045417..5c91933b 100755
--- a/crates/agentfs-cli/tests/all.sh
+++ b/crates/vfs-cli/tests/all.sh
@@ -1,11 +1,11 @@
#!/bin/sh
set -u
-# Set AGENTFS_GATE_FORCE_SKIP=