From 34854e84423fe8b6e57452c03cbacdf55b329d52 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Wed, 24 Dec 2025 21:11:26 +0400 Subject: [PATCH 01/11] temporary use turso Rust bindings from branch --- .gitignore | 2 + cli/Cargo.lock | 563 +++++++++++++++++++++++++++++++++++++++++-- cli/Cargo.toml | 4 +- cli/src/cmd/mount.rs | 2 +- sandbox/Cargo.lock | 311 ++++++++++++++++++++++-- sdk/rust/Cargo.toml | 2 +- 6 files changed, 845 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index 55cdaa43..4ccac128 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ Icon[ ] Network Trash Folder Temporary Items .apdisk + +.agentfs diff --git a/cli/Cargo.lock b/cli/Cargo.lock index ac8df8f6..bfc6d8fa 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -218,6 +218,12 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atty" version = "0.2.14" @@ -235,6 +241,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bincode" version = "1.3.3" @@ -244,6 +256,29 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.10.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.111", + "which", +] + [[package]] name = "bit-vec" version = "0.4.4" @@ -346,6 +381,15 @@ dependencies = [ "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -388,6 +432,17 @@ dependencies = [ "inout", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "3.2.25" @@ -527,6 +582,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -683,6 +748,25 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "env_filter", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -745,6 +829,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -752,7 +845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -766,6 +859,12 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -893,6 +992,21 @@ dependencies = [ "slab", ] +[[package]] +name = "genawaiter" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0" +dependencies = [ + "genawaiter-macro", +] + +[[package]] +name = "genawaiter-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc" + [[package]] name = "generic-array" version = "0.14.7" @@ -959,6 +1073,12 @@ dependencies = [ "url", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "goblin" version = "0.10.4" @@ -1030,6 +1150,112 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.64" @@ -1240,6 +1466,24 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.16" @@ -1282,6 +1526,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.178" @@ -1370,6 +1620,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -1461,6 +1717,12 @@ dependencies = [ "libmimalloc-sys", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1482,6 +1744,23 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "never-say-never" version = "6.6.666" @@ -1533,6 +1812,16 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1609,6 +1898,50 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -1758,6 +2091,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.111", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1806,6 +2149,29 @@ dependencies = [ "rustix 0.36.17", ] +[[package]] +name = "prost" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "quote" version = "1.0.42" @@ -2064,6 +2430,12 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -2098,6 +2470,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + [[package]] name = "rustix" version = "1.1.3" @@ -2149,6 +2534,15 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2175,6 +2569,29 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.10.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.228" @@ -2570,6 +2987,16 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.17" @@ -2595,6 +3022,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.44" @@ -2685,23 +3118,34 @@ dependencies = [ ] [[package]] -name = "turso" -version = "0.4.0-pre.19" +name = "try-lock" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f927be684ce9c612d2f11d04b9d48f4ba553fa98cf6e3bd2073c16602c8d7dc4" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "turso" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", "mimalloc", "thiserror 2.0.17", + "tokio", "tracing", "tracing-subscriber", - "turso_core", + "turso_sdk_kit", + "turso_sync_sdk_kit", ] [[package]] name = "turso_core" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88848a55b47fde014039f36dad09f33af34ad27ed94e70161fb8cf435ea3f3" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" dependencies = [ "aegis", "aes", @@ -2732,7 +3176,7 @@ dependencies = [ "regex", "regex-syntax", "roaring", - "rustc-hash", + "rustc-hash 2.1.1", "rustix 1.1.3", "ryu", "simsimd", @@ -2752,9 +3196,8 @@ dependencies = [ [[package]] name = "turso_ext" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e03badf97da72c41c3289ac19245109096e6262017151dad23ecf7115ee433" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" dependencies = [ "chrono", "getrandom 0.3.4", @@ -2763,9 +3206,8 @@ dependencies = [ [[package]] name = "turso_macros" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681754e341a0420494203b73e067b0b69b29ccf0852d0d24bc7d5255d4441dea" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" dependencies = [ "proc-macro2", "quote", @@ -2774,9 +3216,8 @@ dependencies = [ [[package]] name = "turso_parser" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92dd528809dfb32facb949b9b5416a33ee8f856b444f882854c0835428f511dd" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" dependencies = [ "bitflags 2.10.0", "miette", @@ -2786,6 +3227,69 @@ dependencies = [ "turso_macros", ] +[[package]] +name = "turso_sdk_kit" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +dependencies = [ + "bindgen", + "env_logger", + "tracing", + "tracing-appender", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit_macros", +] + +[[package]] +name = "turso_sdk_kit_macros" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "turso_sync_engine" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +dependencies = [ + "base64", + "bytes", + "genawaiter", + "http", + "libc", + "prost", + "roaring", + "serde", + "serde_json", + "thiserror 2.0.17", + "tracing", + "turso_core", + "turso_parser", + "uuid", +] + +[[package]] +name = "turso_sync_sdk_kit" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +dependencies = [ + "bindgen", + "env_logger", + "genawaiter", + "parking_lot", + "tracing", + "tracing-appender", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit", + "turso_sdk_kit_macros", + "turso_sync_engine", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -2878,7 +3382,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38290439f8459ba56c4bf15fc776463f495fefc4f0112f87a1a075540441b083" dependencies = [ - "foreign-types", + "foreign-types 0.5.0", "libc", "unwind-sys", ] @@ -2947,6 +3451,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -3007,6 +3520,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "winapi" version = "0.2.8" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 0bdbe871..c97241a6 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -17,7 +17,9 @@ agentfs-sdk = { path = "../sdk/rust" } tokio = { version = "1", features = ["full"] } clap = { version = "4", features = ["derive"] } anyhow = "1.0" -turso = "0.4.0-pre.17" +turso = { git = "https://github.com/tursodatabase/turso", branch = "rust-bindings-sync", features = [ + "sync", +] } serde = { version = "1.0", features = ["derive"] } parking_lot = "0.12.5" clap_complete = { version = "=4.5.61", features = ["unstable-dynamic"] } diff --git a/cli/src/cmd/mount.rs b/cli/src/cmd/mount.rs index ebc2bbe0..a6d80c3f 100644 --- a/cli/src/cmd/mount.rs +++ b/cli/src/cmd/mount.rs @@ -1,7 +1,7 @@ use agentfs_sdk::{AgentFS, AgentFSOptions, FileSystem, HostFS, OverlayFS}; use anyhow::Result; use std::{os::unix::fs::MetadataExt, path::PathBuf, sync::Arc}; -use turso::Value; +use turso::value::Value; use crate::fuse::FuseMountOptions; diff --git a/sandbox/Cargo.lock b/sandbox/Cargo.lock index c78eee92..c5c2a729 100644 --- a/sandbox/Cargo.lock +++ b/sandbox/Cargo.lock @@ -160,6 +160,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bincode" version = "1.3.3" @@ -169,6 +175,29 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.10.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.111", + "which", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -277,6 +306,15 @@ dependencies = [ "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -319,6 +357,17 @@ dependencies = [ "inout", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "3.2.25" @@ -535,6 +584,25 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "env_filter", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -723,6 +791,21 @@ dependencies = [ "slab", ] +[[package]] +name = "genawaiter" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0" +dependencies = [ + "genawaiter-macro", +] + +[[package]] +name = "genawaiter-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc" + [[package]] name = "generic-array" version = "0.14.7" @@ -789,6 +872,12 @@ dependencies = [ "url", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "goblin" version = "0.10.4" @@ -860,6 +949,25 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + [[package]] name = "iana-time-zone" version = "0.1.64" @@ -1055,6 +1163,24 @@ dependencies = [ "libc", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.16" @@ -1097,6 +1223,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.178" @@ -1174,6 +1306,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -1265,6 +1403,12 @@ dependencies = [ "libmimalloc-sys", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1305,6 +1449,16 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1497,6 +1651,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.111", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1564,6 +1728,29 @@ dependencies = [ "unarray", ] +[[package]] +name = "prost" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -1817,6 +2004,12 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -1851,6 +2044,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + [[package]] name = "rustix" version = "1.1.3" @@ -2445,22 +2651,21 @@ dependencies = [ [[package]] name = "turso" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f927be684ce9c612d2f11d04b9d48f4ba553fa98cf6e3bd2073c16602c8d7dc4" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" dependencies = [ "mimalloc", "thiserror 2.0.17", "tracing", "tracing-subscriber", - "turso_core", + "turso_sdk_kit", + "turso_sync_sdk_kit", ] [[package]] name = "turso_core" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88848a55b47fde014039f36dad09f33af34ad27ed94e70161fb8cf435ea3f3" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" dependencies = [ "aegis", "aes", @@ -2491,7 +2696,7 @@ dependencies = [ "regex", "regex-syntax", "roaring", - "rustc-hash", + "rustc-hash 2.1.1", "rustix 1.1.3", "ryu", "simsimd", @@ -2511,9 +2716,8 @@ dependencies = [ [[package]] name = "turso_ext" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e03badf97da72c41c3289ac19245109096e6262017151dad23ecf7115ee433" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" dependencies = [ "chrono", "getrandom 0.3.4", @@ -2522,9 +2726,8 @@ dependencies = [ [[package]] name = "turso_macros" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681754e341a0420494203b73e067b0b69b29ccf0852d0d24bc7d5255d4441dea" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" dependencies = [ "proc-macro2", "quote", @@ -2533,9 +2736,8 @@ dependencies = [ [[package]] name = "turso_parser" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92dd528809dfb32facb949b9b5416a33ee8f856b444f882854c0835428f511dd" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" dependencies = [ "bitflags 2.10.0", "miette", @@ -2545,6 +2747,69 @@ dependencies = [ "turso_macros", ] +[[package]] +name = "turso_sdk_kit" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +dependencies = [ + "bindgen", + "env_logger", + "tracing", + "tracing-appender", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit_macros", +] + +[[package]] +name = "turso_sdk_kit_macros" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "turso_sync_engine" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +dependencies = [ + "base64", + "bytes", + "genawaiter", + "http", + "libc", + "prost", + "roaring", + "serde", + "serde_json", + "thiserror 2.0.17", + "tracing", + "turso_core", + "turso_parser", + "uuid", +] + +[[package]] +name = "turso_sync_sdk_kit" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +dependencies = [ + "bindgen", + "env_logger", + "genawaiter", + "parking_lot", + "tracing", + "tracing-appender", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit", + "turso_sdk_kit_macros", + "turso_sync_engine", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -2775,6 +3040,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "winapi" version = "0.2.8" diff --git a/sdk/rust/Cargo.toml b/sdk/rust/Cargo.toml index 9b3e8b2a..5c834d06 100644 --- a/sdk/rust/Cargo.toml +++ b/sdk/rust/Cargo.toml @@ -6,7 +6,7 @@ description = "AgentFS SDK for Rust" license = "MIT" [dependencies] -turso = "0.4.0-pre.17" +turso = { git = "https://github.com/tursodatabase/turso", branch = "rust-bindings-sync" } tokio = { version = "1", features = ["full"] } async-trait = "0.1" serde = { version = "1.0", features = ["derive"] } From 8b59e7f1bc214da45b44b43e03e44b442f7b03d8 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Wed, 24 Dec 2025 22:17:27 +0400 Subject: [PATCH 02/11] slighty refactor sdk --- sdk/rust/src/lib.rs | 75 ++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/sdk/rust/src/lib.rs b/sdk/rust/src/lib.rs index 5fcc3fb4..a5aad86c 100644 --- a/sdk/rust/src/lib.rs +++ b/sdk/rust/src/lib.rs @@ -38,6 +38,39 @@ pub struct AgentFSOptions { } impl AgentFSOptions { + /// Validates an agent ID to prevent path traversal and ensure safe filesystem operations. + /// Returns true if the ID contains only alphanumeric characters, hyphens, and underscores. + pub fn validate_agent_id(id: &str) -> bool { + !id.is_empty() + && id + .chars() + .all(|c| c.is_alphanumeric() || c == '-' || c == '_') + } + pub fn db_path(&self) -> anyhow::Result { + // Determine database path: path takes precedence over id + if let Some(path) = &self.path { + // Custom path provided directly + Ok(path.to_string()) + } else if let Some(id) = &self.id { + // Validate agent ID to prevent path traversal attacks + if !Self::validate_agent_id(&id) { + anyhow::bail!( + "Invalid agent ID '{}'. Agent IDs must contain only alphanumeric characters, hyphens, and underscores.", + id + ); + } + + // Ensure .agentfs directory exists + let agentfs_dir = agentfs_dir(); + if !agentfs_dir.exists() { + std::fs::create_dir_all(agentfs_dir)?; + } + Ok(format!("{}/{}.db", agentfs_dir.display(), id)) + } else { + // No id or path = ephemeral in-memory database + Ok(":memory:".to_string()) + } + } /// Create options for a persistent agent with the given ID pub fn with_id(id: impl Into) -> Self { Self { @@ -78,7 +111,7 @@ impl AgentFSOptions { } // First, check if it's a valid agent ID with an existing database in .agentfs/ - if AgentFS::validate_agent_id(&id_or_path) { + if AgentFSOptions::validate_agent_id(&id_or_path) { let db_path = agentfs_dir().join(format!("{}.db", id_or_path)); if db_path.exists() { return Ok(Self::with_path(db_path.to_str().ok_or_else(|| { @@ -93,7 +126,7 @@ impl AgentFSOptions { Ok(Self::with_path(id_or_path)) } else { // Not a valid agent and not an existing file - if AgentFS::validate_agent_id(&id_or_path) { + if AgentFSOptions::validate_agent_id(&id_or_path) { anyhow::bail!( "Agent '{}' not found at '{}'", id_or_path, @@ -140,32 +173,13 @@ impl AgentFS { /// # } /// ``` pub async fn open(options: AgentFSOptions) -> Result { - // Determine database path: path takes precedence over id - let db_path = if let Some(path) = options.path { - // Custom path provided directly - path - } else if let Some(id) = options.id { - // Validate agent ID to prevent path traversal attacks - if !Self::validate_agent_id(&id) { - anyhow::bail!( - "Invalid agent ID '{}'. Agent IDs must contain only alphanumeric characters, hyphens, and underscores.", - id - ); - } - - // Ensure .agentfs directory exists - let agentfs_dir = agentfs_dir(); - if !agentfs_dir.exists() { - std::fs::create_dir_all(agentfs_dir)?; - } - format!("{}/{}.db", agentfs_dir.display(), id) - } else { - // No id or path = ephemeral in-memory database - ":memory:".to_string() - }; - + let db_path = options.db_path()?; let db = Builder::new_local(&db_path).build().await?; let conn = db.connect()?; + Self::open_with(conn).await + } + + pub async fn open_with(conn: Connection) -> Result { let conn = Arc::new(conn); let kv = KvStore::from_connection(conn.clone()).await?; @@ -399,15 +413,6 @@ impl AgentFS { Err(_) => Ok(None), // Table doesn't exist } } - - /// Validates an agent ID to prevent path traversal and ensure safe filesystem operations. - /// Returns true if the ID contains only alphanumeric characters, hyphens, and underscores. - pub fn validate_agent_id(id: &str) -> bool { - !id.is_empty() - && id - .chars() - .all(|c| c.is_alphanumeric() || c == '-' || c == '_') - } } #[cfg(test)] From 41dd26918842a92621292fe4b3360bdfc6bd62b8 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Wed, 24 Dec 2025 22:37:03 +0400 Subject: [PATCH 03/11] add sync support in the init command --- cli/Cargo.lock | 31 +++++++++++++++++++------------ cli/Cargo.toml | 3 ++- cli/src/cmd/init.rs | 36 ++++++++++++++++++++++++++++++++---- cli/src/main.rs | 35 ++++++++++++++++++++++------------- cli/src/parser.rs | 44 ++++++++++++++++++++++++++++++++++++-------- sdk/rust/Cargo.toml | 2 +- 6 files changed, 112 insertions(+), 39 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index bfc6d8fa..dd6899e5 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -89,6 +89,7 @@ dependencies = [ "reverie-process", "reverie-ptrace", "serde", + "serde_json", "tempfile", "tokio", "tracing-subscriber", @@ -2624,15 +2625,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.146" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "217ca874ae0207aac254aa02c957ded05585a90892cc8d87f9e5fa49669dadd8" +checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -3126,7 +3127,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "turso" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "bytes", "http-body-util", @@ -3145,7 +3146,7 @@ dependencies = [ [[package]] name = "turso_core" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "aegis", "aes", @@ -3197,7 +3198,7 @@ dependencies = [ [[package]] name = "turso_ext" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "chrono", "getrandom 0.3.4", @@ -3207,7 +3208,7 @@ dependencies = [ [[package]] name = "turso_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "proc-macro2", "quote", @@ -3217,7 +3218,7 @@ dependencies = [ [[package]] name = "turso_parser" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "bitflags 2.10.0", "miette", @@ -3230,7 +3231,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "bindgen", "env_logger", @@ -3244,7 +3245,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "proc-macro2", "quote", @@ -3254,7 +3255,7 @@ dependencies = [ [[package]] name = "turso_sync_engine" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "base64", "bytes", @@ -3275,7 +3276,7 @@ dependencies = [ [[package]] name = "turso_sync_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "bindgen", "env_logger", @@ -4030,3 +4031,9 @@ dependencies = [ "quote", "syn 2.0.111", ] + +[[package]] +name = "zmij" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e404bcd8afdaf006e529269d3e85a743f9480c3cef60034d77860d02964f3ba" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index c97241a6..7df9844e 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -17,13 +17,14 @@ agentfs-sdk = { path = "../sdk/rust" } tokio = { version = "1", features = ["full"] } clap = { version = "4", features = ["derive"] } anyhow = "1.0" -turso = { git = "https://github.com/tursodatabase/turso", branch = "rust-bindings-sync", features = [ +turso = { git = "https://github.com/tursodatabase/turso", rev = "5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee", features = [ "sync", ] } serde = { version = "1.0", features = ["derive"] } parking_lot = "0.12.5" clap_complete = { version = "=4.5.61", features = ["unstable-dynamic"] } dirs = "6" +serde_json = "1.0.147" # Unix dependencies [target.'cfg(unix)'.dependencies] diff --git a/cli/src/cmd/init.rs b/cli/src/cmd/init.rs index 11a1dfc5..91e808f4 100644 --- a/cli/src/cmd/init.rs +++ b/cli/src/cmd/init.rs @@ -4,8 +4,11 @@ use std::time::{SystemTime, UNIX_EPOCH}; use agentfs_sdk::{agentfs_dir, AgentFS, AgentFSOptions, OverlayFS}; use anyhow::{Context, Result as AnyhowResult}; +use crate::parser::SyncConfig; + pub async fn init_database( id: Option, + sync_config_path: Option, force: bool, base: Option, ) -> AnyhowResult<()> { @@ -19,7 +22,7 @@ pub async fn init_database( }); // Validate agent ID for safety - if !AgentFS::validate_agent_id(&id) { + if !AgentFSOptions::validate_agent_id(&id) { anyhow::bail!( "Invalid agent ID '{}'. Agent IDs must contain only alphanumeric characters, hyphens, and underscores.", id @@ -59,9 +62,26 @@ pub async fn init_database( // Use the SDK to initialize the database - this ensures consistency // The SDK will create .agentfs directory and database file - let agent = AgentFS::open(AgentFSOptions::with_id(&id)) - .await - .context("Failed to initialize database")?; + let sync_config = SyncConfig::parse(sync_config_path)?; + let options = AgentFSOptions::with_id(&id); + let mut synced_db = None; + let agent = if let Some(sync) = sync_config { + let mut builder = turso::sync::Builder::new_remote(&options.db_path()?, &sync.remote_url); + if let Some(auth_token) = sync.auth_token { + builder = builder.with_auth_token(auth_token); + } + let db = builder.build().await?; + let conn = db.connect().await?; + let agent = AgentFS::open_with(conn) + .await + .context("Failed to initialize synced database")?; + synced_db = Some(db); + agent + } else { + AgentFS::open(options) + .await + .context("Failed to initialize database")? + }; // If base is provided, initialize the overlay schema using the SDK if let Some(base_path) = base { @@ -76,10 +96,18 @@ pub async fn init_database( .await .context("Failed to initialize overlay schema")?; + if let Some(synced_db) = synced_db { + synced_db.push().await?; + } + eprintln!("Created overlay filesystem: {}", db_path.display()); eprintln!("Agent ID: {}", id); eprintln!("Base: {}", base_path_str); } else { + if let Some(synced_db) = synced_db { + synced_db.push().await?; + } + eprintln!("Created agent filesystem: {}", db_path.display()); eprintln!("Agent ID: {}", id); } diff --git a/cli/src/main.rs b/cli/src/main.rs index b0a845b4..325b9be7 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -4,7 +4,7 @@ use clap_complete::CompleteEnv; use agentfs::{ cmd::{self, completions::handle_completions}, get_runtime, - parser::{Args, Command, FsCommand}, + parser::{Args, Command, FsCommand, SyncConfig}, }; fn main() { @@ -14,9 +14,15 @@ fn main() { let args = Args::parse(); match args.command { - Command::Init { id, force, base } => { + Command::Init { + id, + sync_config_path, + force, + base, + } => { let rt = get_runtime(); - if let Err(e) = rt.block_on(cmd::init::init_database(id, force, base)) { + if let Err(e) = rt.block_on(cmd::init::init_database(id, sync_config_path, force, base)) + { eprintln!("Error: {}", e); std::process::exit(1); } @@ -44,6 +50,7 @@ fn main() { } Command::Mount { id_or_path, + sync_config_path, mountpoint, auto_unmount, allow_root, @@ -64,20 +71,24 @@ fn main() { std::process::exit(1); } } - Command::Diff { id_or_path } => { + Command::Diff { + id_or_path, + sync_config_path, + } => { let rt = get_runtime(); if let Err(e) = rt.block_on(cmd::fs::diff_filesystem(id_or_path)) { eprintln!("Error: {}", e); std::process::exit(1); } } - Command::Fs { command } => { + Command::Fs { + command, + id_or_path, + sync_config_path, + } => { let rt = get_runtime(); match command { - FsCommand::Ls { - id_or_path, - fs_path, - } => { + FsCommand::Ls { fs_path } => { if let Err(e) = rt.block_on(cmd::fs::ls_filesystem( &mut std::io::stdout(), id_or_path, @@ -87,10 +98,7 @@ fn main() { std::process::exit(1); } } - FsCommand::Cat { - id_or_path, - file_path, - } => { + FsCommand::Cat { file_path } => { if let Err(e) = rt.block_on(cmd::fs::cat_filesystem( &mut std::io::stdout(), id_or_path, @@ -106,6 +114,7 @@ fn main() { #[cfg(unix)] Command::Nfs { id_or_path, + sync_config_path, bind, port, } => { diff --git a/cli/src/parser.rs b/cli/src/parser.rs index d20adef9..851cf040 100644 --- a/cli/src/parser.rs +++ b/cli/src/parser.rs @@ -4,6 +4,7 @@ use clap::{Parser, Subcommand}; use clap_complete::{ engine::ValueCompleter, ArgValueCompleter, CompletionCandidate, PathCompleter, }; +use serde::Deserialize; use std::path::{Path, PathBuf}; #[derive(Parser, Debug)] @@ -15,6 +16,22 @@ pub struct Args { pub command: Command, } +#[derive(Debug, Deserialize)] +pub struct SyncConfig { + pub remote_url: String, + pub auth_token: Option, +} + +impl SyncConfig { + pub fn parse(path: Option) -> anyhow::Result> { + let Some(path) = path else { + return Ok(None); + }; + let data = std::fs::read_to_string(path)?; + Ok(Some(serde_json::from_str(&data)?)) + } +} + #[derive(Subcommand, Debug)] pub enum Command { /// Manage shell completions @@ -34,9 +51,19 @@ pub enum Command { /// Base directory for overlay filesystem (copy-on-write) #[arg(long)] base: Option, + + #[arg(long)] + sync_config_path: Option, }, /// Filesystem operations Fs { + /// Agent ID or database path + #[arg(add = ArgValueCompleter::new(id_or_path_completer))] + id_or_path: String, + + #[arg(long)] + sync_config_path: Option, + #[command(subcommand)] command: FsCommand, }, @@ -76,6 +103,9 @@ pub enum Command { #[arg(value_name = "ID_OR_PATH", add = ArgValueCompleter::new(id_or_path_completer))] id_or_path: String, + #[arg(long)] + sync_config_path: Option, + /// Mount point directory #[arg(value_name = "MOUNTPOINT", add = ArgValueCompleter::new(PathCompleter::dir()))] mountpoint: PathBuf, @@ -105,6 +135,9 @@ pub enum Command { /// Agent ID or database path #[arg(value_name = "ID_OR_PATH", add = ArgValueCompleter::new(id_or_path_completer))] id_or_path: String, + + #[arg(long)] + sync_config_path: Option, }, /// Start an NFS server to export an AgentFS filesystem over the network #[cfg(unix)] @@ -113,6 +146,9 @@ pub enum Command { #[arg(value_name = "ID_OR_PATH", add = ArgValueCompleter::new(id_or_path_completer))] id_or_path: String, + #[arg(long)] + sync_config_path: Option, + /// IP address to bind to #[arg(long, default_value = "127.0.0.1")] bind: String, @@ -127,20 +163,12 @@ pub enum Command { pub enum FsCommand { /// List files in the filesystem Ls { - /// Agent ID or database path - #[arg(add = ArgValueCompleter::new(id_or_path_completer))] - id_or_path: String, - /// Path to list (default: /) #[arg(default_value = "/")] fs_path: String, }, /// Display file contents Cat { - /// Agent ID or database path - #[arg(add = ArgValueCompleter::new(id_or_path_completer))] - id_or_path: String, - /// Path to the file in the filesystem file_path: String, }, diff --git a/sdk/rust/Cargo.toml b/sdk/rust/Cargo.toml index 5c834d06..43f72cca 100644 --- a/sdk/rust/Cargo.toml +++ b/sdk/rust/Cargo.toml @@ -6,7 +6,7 @@ description = "AgentFS SDK for Rust" license = "MIT" [dependencies] -turso = { git = "https://github.com/tursodatabase/turso", branch = "rust-bindings-sync" } +turso = { git = "https://github.com/tursodatabase/turso", rev = "5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" } tokio = { version = "1", features = ["full"] } async-trait = "0.1" serde = { version = "1.0", features = ["derive"] } From 400510a11dc6fc4d056dbbfca4cd2b8416e88713 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Thu, 25 Dec 2025 11:11:49 +0400 Subject: [PATCH 04/11] propagate optional synced_db param to the FUSE layer --- cli/src/cmd/init.rs | 46 ++++++++++++++++++++++++++------------------ cli/src/cmd/mount.rs | 10 ++++++---- cli/src/fuse.rs | 13 +++++++++++-- cli/src/main.rs | 1 + 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/cli/src/cmd/init.rs b/cli/src/cmd/init.rs index 91e808f4..80a6df71 100644 --- a/cli/src/cmd/init.rs +++ b/cli/src/cmd/init.rs @@ -6,6 +6,32 @@ use anyhow::{Context, Result as AnyhowResult}; use crate::parser::SyncConfig; +pub async fn create_agentfs( + options: AgentFSOptions, + sync_config_path: Option, +) -> anyhow::Result<(Option, AgentFS)> { + let sync_config = SyncConfig::parse(sync_config_path)?; + if let Some(sync) = sync_config { + let mut builder = turso::sync::Builder::new_remote(&options.db_path()?, &sync.remote_url); + if let Some(auth_token) = sync.auth_token { + builder = builder.with_auth_token(auth_token); + } + let db = builder.build().await?; + let conn = db.connect().await?; + let agent = AgentFS::open_with(conn) + .await + .context("Failed to initialize synced database")?; + Ok((Some(db), agent)) + } else { + Ok(( + None, + AgentFS::open(options) + .await + .context("Failed to initialize database")?, + )) + } +} + pub async fn init_database( id: Option, sync_config_path: Option, @@ -62,26 +88,8 @@ pub async fn init_database( // Use the SDK to initialize the database - this ensures consistency // The SDK will create .agentfs directory and database file - let sync_config = SyncConfig::parse(sync_config_path)?; let options = AgentFSOptions::with_id(&id); - let mut synced_db = None; - let agent = if let Some(sync) = sync_config { - let mut builder = turso::sync::Builder::new_remote(&options.db_path()?, &sync.remote_url); - if let Some(auth_token) = sync.auth_token { - builder = builder.with_auth_token(auth_token); - } - let db = builder.build().await?; - let conn = db.connect().await?; - let agent = AgentFS::open_with(conn) - .await - .context("Failed to initialize synced database")?; - synced_db = Some(db); - agent - } else { - AgentFS::open(options) - .await - .context("Failed to initialize database")? - }; + let (synced_db, agent) = create_agentfs(options, sync_config_path).await?; // If base is provided, initialize the overlay schema using the SDK if let Some(base_path) = base { diff --git a/cli/src/cmd/mount.rs b/cli/src/cmd/mount.rs index a6d80c3f..9403a39b 100644 --- a/cli/src/cmd/mount.rs +++ b/cli/src/cmd/mount.rs @@ -1,15 +1,17 @@ -use agentfs_sdk::{AgentFS, AgentFSOptions, FileSystem, HostFS, OverlayFS}; +use agentfs_sdk::{AgentFSOptions, FileSystem, HostFS, OverlayFS}; use anyhow::Result; use std::{os::unix::fs::MetadataExt, path::PathBuf, sync::Arc}; use turso::value::Value; -use crate::fuse::FuseMountOptions; +use crate::{cmd::init::create_agentfs, fuse::FuseMountOptions}; /// Arguments for the mount command. #[derive(Debug, Clone)] pub struct MountArgs { /// The agent filesystem ID or path. pub id_or_path: String, + /// optional sync config path + pub sync_config_path: Option, /// The mountpoint path. pub mountpoint: PathBuf, /// Automatically unmount when the process exits. @@ -63,7 +65,7 @@ pub fn mount(args: MountArgs) -> Result<()> { let mount = move || { let rt = crate::get_runtime(); - let agentfs = rt.block_on(AgentFS::open(opts))?; + let (db, agentfs) = rt.block_on(create_agentfs(opts, args.sync_config_path))?; // Check for overlay configuration let fs: Arc = rt.block_on(async { @@ -100,7 +102,7 @@ pub fn mount(args: MountArgs) -> Result<()> { } })?; - crate::fuse::mount(fs, fuse_opts, rt) + crate::fuse::mount(fs, db, fuse_opts, rt) }; if args.foreground { diff --git a/cli/src/fuse.rs b/cli/src/fuse.rs index 22e2dd5c..6bf97988 100644 --- a/cli/src/fuse.rs +++ b/cli/src/fuse.rs @@ -50,6 +50,7 @@ struct OpenFile { struct AgentFSFuse { fs: Arc, + synced_db: Option, runtime: Runtime, path_cache: Arc>>, /// Maps file handle -> open file state @@ -1037,9 +1038,16 @@ impl AgentFSFuse { /// /// The uid and gid are used for all file ownership to avoid "dubious ownership" /// errors from tools like git that check file ownership. - fn new(fs: Arc, runtime: Runtime, uid: u32, gid: u32) -> Self { + fn new( + fs: Arc, + synced_db: Option, + runtime: Runtime, + uid: u32, + gid: u32, + ) -> Self { Self { fs, + synced_db, runtime, path_cache: Arc::new(Mutex::new(HashMap::new())), open_files: Arc::new(Mutex::new(HashMap::new())), @@ -1147,6 +1155,7 @@ fn fillattr(stats: &Stats, uid: u32, gid: u32) -> FileAttr { pub fn mount( fs: Arc, + synced_db: Option, opts: FuseMountOptions, runtime: Runtime, ) -> anyhow::Result<()> { @@ -1155,7 +1164,7 @@ pub fn mount( let uid = opts.uid.unwrap_or_else(|| unsafe { libc::getuid() }); let gid = opts.gid.unwrap_or_else(|| unsafe { libc::getgid() }); - let fs = AgentFSFuse::new(fs, runtime, uid, gid); + let fs = AgentFSFuse::new(fs, synced_db, runtime, uid, gid); fs.add_path(1, "/".to_string()); diff --git a/cli/src/main.rs b/cli/src/main.rs index 325b9be7..bf8737fb 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -60,6 +60,7 @@ fn main() { } => { if let Err(e) = cmd::mount(cmd::MountArgs { id_or_path, + sync_config_path, mountpoint, auto_unmount, allow_root, From 6213c6962607d7cc7d11d551ac87bb79063dd9fb Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Thu, 25 Dec 2025 11:17:38 +0400 Subject: [PATCH 05/11] add logging to the fuse layer --- cli/Cargo.lock | 1 + cli/Cargo.toml | 2 + cli/src/fuse.rs | 107 +++++++++++++++++++++++++++------------------ cli/src/main.rs | 18 +++++++- sandbox/Cargo.lock | 18 ++++---- 5 files changed, 94 insertions(+), 52 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index dd6899e5..2e34406c 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -92,6 +92,7 @@ dependencies = [ "serde_json", "tempfile", "tokio", + "tracing", "tracing-subscriber", "turso", "uuid", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 7df9844e..f91ec5d1 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -25,6 +25,8 @@ parking_lot = "0.12.5" clap_complete = { version = "=4.5.61", features = ["unstable-dynamic"] } dirs = "6" serde_json = "1.0.147" +tracing = "0.1.44" +tracing-subscriber = "0.3" # Unix dependencies [target.'cfg(unix)'.dependencies] diff --git a/cli/src/fuse.rs b/cli/src/fuse.rs index 6bf97988..fa434a5d 100644 --- a/cli/src/fuse.rs +++ b/cli/src/fuse.rs @@ -43,9 +43,9 @@ pub struct FuseMountOptions { } /// Tracks an open file handle -struct OpenFile { +enum OpenFile { /// The file handle from the filesystem layer. - file: BoxedFile, + File { file: BoxedFile }, } struct AgentFSFuse { @@ -77,6 +77,7 @@ impl Filesystem for AgentFSFuse { /// - No opendir support: skips opendir/releasedir calls since we don't track /// directory handles, reducing round-trips for directory operations. fn init(&mut self, _req: &Request, config: &mut KernelConfig) -> Result<(), libc::c_int> { + tracing::info!("fuse::init(config={config:?})"); let _ = config.add_capabilities( FUSE_ASYNC_READ | FUSE_WRITEBACK_CACHE @@ -96,6 +97,7 @@ impl Filesystem for AgentFSFuse { /// Resolves `name` under the directory identified by `parent` inode, stats the /// resulting path, and caches the inode-to-path mapping on success. fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + tracing::info!("fuse::lookup(parent={parent}, name={name:?})"); let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -120,7 +122,8 @@ impl Filesystem for AgentFSFuse { /// /// Returns metadata (size, permissions, timestamps, etc.) for the file or /// directory identified by `ino`. Root inode (1) is handled specially. - fn getattr(&mut self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { + fn getattr(&mut self, _req: &Request, ino: u64, fh: Option, reply: ReplyAttr) { + tracing::info!("fuse::getattr(ino={ino}, fh={fh:?})"); let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -141,6 +144,7 @@ impl Filesystem for AgentFSFuse { /// Returns the path that the symlink points to. This is called by operations /// like `ls -l` to display symlink targets. fn readlink(&mut self, _req: &Request, ino: u64, reply: ReplyData) { + tracing::info!("fuse::readlink(ino={ino})"); let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -180,22 +184,24 @@ impl Filesystem for AgentFSFuse { _flags: Option, reply: ReplyAttr, ) { + tracing::info!("fuse::setattr(ino={ino}, size={size:?}, fh={fh:?})"); // Handle truncate if let Some(new_size) = size { let result = if let Some(fh) = fh { // Use file handle if available (ftruncate) let file = { let open_files = self.open_files.lock(); - open_files.get(&fh).map(|f| f.file.clone()) + match open_files.get(&fh) { + Some(OpenFile::File { file }) => file.clone(), + _ => { + reply.error(libc::EBADF); + return; + } + } }; - if let Some(file) = file { - self.runtime - .block_on(async move { file.truncate(new_size).await }) - } else { - reply.error(libc::EBADF); - return; - } + self.runtime + .block_on(async move { file.truncate(new_size).await }) } else { // Open file and truncate via file handle let Some(path) = self.path_cache.lock().get(&ino).cloned() else { @@ -247,10 +253,11 @@ impl Filesystem for AgentFSFuse { &mut self, _req: &Request, ino: u64, - _fh: u64, + fh: u64, offset: i64, mut reply: ReplyDirectory, ) { + tracing::info!("fuse::readdir(ino={ino}, fh={fh:?}, offset={offset})"); let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -346,10 +353,11 @@ impl Filesystem for AgentFSFuse { &mut self, _req: &Request, ino: u64, - _fh: u64, + fh: u64, offset: i64, mut reply: ReplyDirectoryPlus, ) { + tracing::info!("fuse::readdirplus(ino={ino}, fh={fh:?}, offset={offset})"); let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -487,10 +495,11 @@ impl Filesystem for AgentFSFuse { _req: &Request, parent: u64, name: &OsStr, - _mode: u32, - _umask: u32, + mode: u32, + umask: u32, reply: ReplyEntry, ) { + tracing::info!("fuse::mkdir(parent={parent}, name={name:?}, mode={mode}, mask={umask})"); let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -532,6 +541,7 @@ impl Filesystem for AgentFSFuse { /// Verifies the target is a directory and is empty before removal. /// Returns `ENOTDIR` if not a directory, `ENOTEMPTY` if not empty. fn rmdir(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + tracing::info!("fuse::rmdir(parent={parent}, name={name:?})"); let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -611,11 +621,12 @@ impl Filesystem for AgentFSFuse { _req: &Request, parent: u64, name: &OsStr, - _mode: u32, - _umask: u32, - _flags: i32, + mode: u32, + umask: u32, + flags: i32, reply: ReplyCreate, ) { + tracing::info!("fuse::create(parent={parent}, name={name:?}, mode={mode}, umask={umask}, flags={flags})"); let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -669,7 +680,7 @@ impl Filesystem for AgentFSFuse { }; let fh = self.alloc_fh(); - self.open_files.lock().insert(fh, OpenFile { file }); + self.open_files.lock().insert(fh, OpenFile::File { file }); reply.created(&TTL, &attr, 0, fh, 0); } @@ -685,6 +696,9 @@ impl Filesystem for AgentFSFuse { target: &Path, reply: ReplyEntry, ) { + tracing::info!( + "fuse::symlink(parent={parent}, link_name={link_name:?}, target={target:?})" + ); let Some(path) = self.lookup_path(parent, link_name) else { reply.error(libc::ENOENT); return; @@ -730,6 +744,7 @@ impl Filesystem for AgentFSFuse { /// /// Gets the file's inode before removal to clean up the path cache. fn unlink(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + tracing::info!("fuse::unlink(parent={parent}, name={name:?})"); let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -784,9 +799,10 @@ impl Filesystem for AgentFSFuse { name: &OsStr, newparent: u64, newname: &OsStr, - _flags: u32, + flags: u32, reply: ReplyEmpty, ) { + tracing::info!("fuse::rename(parent={parent}, name={name:?}, newparent={newparent}, newname={newname:?}, flags={flags})"); let Some(from_path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -852,7 +868,8 @@ impl Filesystem for AgentFSFuse { /// Opens a file for reading or writing. /// /// Allocates a file handle and opens the file in the filesystem layer. - fn open(&mut self, _req: &Request, ino: u64, _flags: i32, reply: ReplyOpen) { + fn open(&mut self, _req: &Request, ino: u64, flags: i32, reply: ReplyOpen) { + tracing::info!("fuse::open(ino={ino}, flags={flags})"); let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -867,7 +884,7 @@ impl Filesystem for AgentFSFuse { match result { Ok(file) => { let fh = self.alloc_fh(); - self.open_files.lock().insert(fh, OpenFile { file }); + self.open_files.lock().insert(fh, OpenFile::File { file }); reply.opened(fh, 0); } Err(_) => reply.error(libc::EIO), @@ -878,21 +895,22 @@ impl Filesystem for AgentFSFuse { fn read( &mut self, _req: &Request, - _ino: u64, + ino: u64, fh: u64, offset: i64, size: u32, - _flags: i32, - _lock: Option, + flags: i32, + lock: Option, reply: ReplyData, ) { + tracing::info!("fuse::read(ino={ino}, fh={fh:?}, offset={offset}, size={size}, flags={flags}, lock={lock:?})"); let file = { let open_files = self.open_files.lock(); - let Some(open_file) = open_files.get(&fh) else { + let Some(OpenFile::File { file }) = open_files.get(&fh) else { reply.error(libc::EBADF); return; }; - open_file.file.clone() + file.clone() }; let result = self @@ -909,22 +927,23 @@ impl Filesystem for AgentFSFuse { fn write( &mut self, _req: &Request, - _ino: u64, + ino: u64, fh: u64, offset: i64, data: &[u8], - _write_flags: u32, - _flags: i32, - _lock_owner: Option, + write_flags: u32, + flags: i32, + lock_owner: Option, reply: ReplyWrite, ) { + tracing::info!("fuse::write(ino={ino}, fh={fh}, offset={offset}, data.len()={}, write_flags={write_flags}, flags={flags}, lock_owner={lock_owner:?})", data.len()); let file = { let open_files = self.open_files.lock(); - let Some(open_file) = open_files.get(&fh) else { + let Some(OpenFile::File { file }) = open_files.get(&fh) else { reply.error(libc::EBADF); return; }; - open_file.file.clone() + file.clone() }; let data_len = data.len(); @@ -942,7 +961,8 @@ impl Filesystem for AgentFSFuse { /// Flushes data to the backend storage. /// /// Since writes go directly to the database, this is a no-op. - fn flush(&mut self, _req: &Request, _ino: u64, fh: u64, _lock_owner: u64, reply: ReplyEmpty) { + fn flush(&mut self, _req: &Request, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { + tracing::info!("fuse::flush(ino={ino}, fh={fh}, lock_owner={lock_owner})"); let open_files = self.open_files.lock(); if open_files.contains_key(&fh) { reply.ok(); @@ -955,12 +975,13 @@ impl Filesystem for AgentFSFuse { /// /// This now uses the file handle's fsync which knows which layer(s) the /// file exists in, avoiding errors when a file only exists in one layer. - fn fsync(&mut self, _req: &Request, _ino: u64, fh: u64, _datasync: bool, reply: ReplyEmpty) { + fn fsync(&mut self, _req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { + tracing::info!("fuse::fsync(ino={ino}, fh={fh}, datasync={datasync})"); let file = { let open_files = self.open_files.lock(); match open_files.get(&fh) { - Some(open_file) => open_file.file.clone(), - None => { + Some(OpenFile::File { file }) => file.clone(), + _ => { reply.error(libc::EBADF); return; } @@ -982,13 +1003,14 @@ impl Filesystem for AgentFSFuse { fn release( &mut self, _req: &Request, - _ino: u64, + ino: u64, fh: u64, - _flags: i32, - _lock_owner: Option, - _flush: bool, + flags: i32, + lock_owner: Option, + flush: bool, reply: ReplyEmpty, ) { + tracing::info!("fuse::release(ino={ino}, fh={fh}, flags={flags}, lock_owner={lock_owner:?}, flush={flush})"); self.open_files.lock().remove(&fh); reply.ok(); } @@ -996,7 +1018,8 @@ impl Filesystem for AgentFSFuse { /// Returns filesystem statistics. /// /// Queries actual usage from the SDK and reports it to tools like `df`. - fn statfs(&mut self, _req: &Request, _ino: u64, reply: ReplyStatfs) { + fn statfs(&mut self, _req: &Request, ino: u64, reply: ReplyStatfs) { + tracing::info!("fuse::statfs(ino={ino})"); const BLOCK_SIZE: u64 = 4096; const TOTAL_INODES: u64 = 1_000_000; // Virtual limit const MAX_NAMELEN: u32 = 255; diff --git a/cli/src/main.rs b/cli/src/main.rs index bf8737fb..dd168a3b 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -4,8 +4,9 @@ use clap_complete::CompleteEnv; use agentfs::{ cmd::{self, completions::handle_completions}, get_runtime, - parser::{Args, Command, FsCommand, SyncConfig}, + parser::{Args, Command, FsCommand}, }; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; fn main() { reset_sigpipe(); @@ -13,6 +14,21 @@ fn main() { CompleteEnv::with_factory(Args::command).complete(); let args = Args::parse(); + let default_env_filter = tracing_subscriber::EnvFilter::builder() + .with_default_directive(tracing::level_filters::LevelFilter::ERROR.into()) + .from_env_lossy(); + if let Err(e) = tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .with_line_number(true) + .with_thread_ids(true), + ) + .with(default_env_filter.add_directive("rustyline=off".parse().unwrap())) + .try_init() + { + println!("Unable to setup tracing appender: {e:?}"); + } + match args.command { Command::Init { id, diff --git a/sandbox/Cargo.lock b/sandbox/Cargo.lock index c5c2a729..fc83896b 100644 --- a/sandbox/Cargo.lock +++ b/sandbox/Cargo.lock @@ -2652,7 +2652,7 @@ dependencies = [ [[package]] name = "turso" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "mimalloc", "thiserror 2.0.17", @@ -2665,7 +2665,7 @@ dependencies = [ [[package]] name = "turso_core" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "aegis", "aes", @@ -2717,7 +2717,7 @@ dependencies = [ [[package]] name = "turso_ext" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "chrono", "getrandom 0.3.4", @@ -2727,7 +2727,7 @@ dependencies = [ [[package]] name = "turso_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "proc-macro2", "quote", @@ -2737,7 +2737,7 @@ dependencies = [ [[package]] name = "turso_parser" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "bitflags 2.10.0", "miette", @@ -2750,7 +2750,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "bindgen", "env_logger", @@ -2764,7 +2764,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "proc-macro2", "quote", @@ -2774,7 +2774,7 @@ dependencies = [ [[package]] name = "turso_sync_engine" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "base64", "bytes", @@ -2795,7 +2795,7 @@ dependencies = [ [[package]] name = "turso_sync_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?branch=rust-bindings-sync#a6cc0201ba2f6560951583165675765119c37b90" +source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" dependencies = [ "bindgen", "env_logger", From 1297f608f81bd25d7db09a8c63303279f16a36fb Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Thu, 25 Dec 2025 12:34:09 +0400 Subject: [PATCH 06/11] add some support of sync to the agentfs --- cli/Cargo.lock | 18 +++---- cli/Cargo.toml | 2 +- cli/src/cmd/fs.rs | 54 ++++++++++--------- cli/src/cmd/init.rs | 4 ++ cli/src/cmd/nfs.rs | 15 ++++-- cli/src/fuse.rs | 125 ++++++++++++++++++++++++++++++++++++++++++++ cli/src/main.rs | 11 +++- cli/src/parser.rs | 2 + sandbox/Cargo.lock | 18 +++---- sdk/rust/Cargo.toml | 2 +- 10 files changed, 198 insertions(+), 53 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 2e34406c..731d8b81 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -3128,7 +3128,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "turso" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "bytes", "http-body-util", @@ -3147,7 +3147,7 @@ dependencies = [ [[package]] name = "turso_core" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "aegis", "aes", @@ -3199,7 +3199,7 @@ dependencies = [ [[package]] name = "turso_ext" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "chrono", "getrandom 0.3.4", @@ -3209,7 +3209,7 @@ dependencies = [ [[package]] name = "turso_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "proc-macro2", "quote", @@ -3219,7 +3219,7 @@ dependencies = [ [[package]] name = "turso_parser" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "bitflags 2.10.0", "miette", @@ -3232,7 +3232,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "bindgen", "env_logger", @@ -3246,7 +3246,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "proc-macro2", "quote", @@ -3256,7 +3256,7 @@ dependencies = [ [[package]] name = "turso_sync_engine" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "base64", "bytes", @@ -3277,7 +3277,7 @@ dependencies = [ [[package]] name = "turso_sync_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "bindgen", "env_logger", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f91ec5d1..705814b4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -17,7 +17,7 @@ agentfs-sdk = { path = "../sdk/rust" } tokio = { version = "1", features = ["full"] } clap = { version = "4", features = ["derive"] } anyhow = "1.0" -turso = { git = "https://github.com/tursodatabase/turso", rev = "5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee", features = [ +turso = { git = "https://github.com/tursodatabase/turso", rev = "bd2b3314a0d860ac8bde2fe560650f7d753591eb", features = [ "sync", ] } serde = { version = "1.0", features = ["derive"] } diff --git a/cli/src/cmd/fs.rs b/cli/src/cmd/fs.rs index c7424c4a..a46aac7f 100644 --- a/cli/src/cmd/fs.rs +++ b/cli/src/cmd/fs.rs @@ -1,8 +1,10 @@ -use std::collections::VecDeque; +use std::{collections::VecDeque, path::PathBuf}; -use agentfs_sdk::{AgentFS, AgentFSOptions}; +use agentfs_sdk::AgentFSOptions; use anyhow::{Context, Result as AnyhowResult}; -use turso::{Builder, Value}; +use turso::Value; + +use crate::cmd::init::create_agentfs; const ROOT_INO: i64 = 1; const S_IFMT: u32 = 0o170000; @@ -13,18 +15,14 @@ const S_IFLNK: u32 = 0o120000; pub async fn ls_filesystem( stdout: &mut impl std::io::Write, id_or_path: String, + sync_config_path: Option, path: &str, ) -> AnyhowResult<()> { let options = AgentFSOptions::resolve(&id_or_path)?; - let db_path = options.path.context("No database path resolved")?; eprintln!("Using agent: {}", id_or_path); - let db = Builder::new_local(&db_path) - .build() - .await - .context("Failed to open filesystem")?; - - let conn = db.connect().context("Failed to connect to filesystem")?; + let (_, agentfs) = create_agentfs(options, sync_config_path).await?; + let conn = agentfs.get_connection(); if path != "/" { anyhow::bail!("Only root directory (/) is currently supported"); @@ -101,17 +99,12 @@ pub async fn ls_filesystem( pub async fn cat_filesystem( stdout: &mut impl std::io::Write, id_or_path: String, + sync_config_path: Option, path: &str, ) -> AnyhowResult<()> { let options = AgentFSOptions::resolve(&id_or_path)?; - let db_path = options.path.context("No database path resolved")?; - - let db = Builder::new_local(&db_path) - .build() - .await - .context("Failed to open filesystem")?; - - let conn = db.connect().context("Failed to connect to filesystem")?; + let (_, agentfs) = create_agentfs(options, sync_config_path).await?; + let conn = agentfs.get_connection(); let path_components: Vec<&str> = path .trim_start_matches('/') @@ -232,11 +225,14 @@ fn path_exists_in_base(base_path: &str, rel_path: &str) -> bool { std::path::Path::new(&full_path).exists() } -pub async fn diff_filesystem(id_or_path: String) -> AnyhowResult<()> { +pub async fn diff_filesystem( + id_or_path: String, + sync_config_path: Option, +) -> AnyhowResult<()> { let options = AgentFSOptions::resolve(&id_or_path)?; eprintln!("Using agent: {}", id_or_path); - let agent = AgentFS::open(options) + let (_, agent) = create_agentfs(options, sync_config_path) .await .context("Failed to open agent")?; @@ -328,7 +324,9 @@ mod tests { pub async fn cat_file_not_found() { let (_agentfs, path, _file) = agentfs().await; let mut buf = Vec::new(); - let err = cat_filesystem(&mut buf, path, "test.md").await.unwrap_err(); + let err = cat_filesystem(&mut buf, path, None, "test.md") + .await + .unwrap_err(); assert!(err.to_string().contains("File not found")); } @@ -338,7 +336,9 @@ mod tests { let content = b"hello, agentfs"; agentfs.fs.write_file("test.md", content).await.unwrap(); let mut buf = Vec::new(); - cat_filesystem(&mut buf, path, "test.md").await.unwrap(); + cat_filesystem(&mut buf, path, None, "test.md") + .await + .unwrap(); assert_eq!(buf, content); } @@ -348,7 +348,9 @@ mod tests { let content = vec![100u8; 4 * 1024 * 1024]; agentfs.fs.write_file("test.md", &content).await.unwrap(); let mut buf = Vec::new(); - cat_filesystem(&mut buf, path, "test.md").await.unwrap(); + cat_filesystem(&mut buf, path, None, "test.md") + .await + .unwrap(); assert_eq!(buf, content); } @@ -356,7 +358,7 @@ mod tests { pub async fn ls_empty() { let (_agentfs, path, _file) = agentfs().await; let mut buf = Vec::new(); - ls_filesystem(&mut buf, path, "/").await.unwrap(); + ls_filesystem(&mut buf, path, None, "/").await.unwrap(); assert_eq!(buf, b""); } @@ -368,7 +370,7 @@ mod tests { let big = vec![100u8; 1024 * 1024]; agentfs.fs.write_file("3.md", &big).await.unwrap(); let mut buf = Vec::new(); - ls_filesystem(&mut buf, path, "/").await.unwrap(); + ls_filesystem(&mut buf, path, None, "/").await.unwrap(); assert_eq!( buf, b"f 1.md @@ -391,7 +393,7 @@ f 3.md let big = vec![100u8; 1024 * 1024]; agentfs.fs.write_file("d/e/3.md", &big).await.unwrap(); let mut buf = Vec::new(); - ls_filesystem(&mut buf, path, "/").await.unwrap(); + ls_filesystem(&mut buf, path, None, "/").await.unwrap(); assert_eq!( buf, b"d a diff --git a/cli/src/cmd/init.rs b/cli/src/cmd/init.rs index 80a6df71..8803ff4e 100644 --- a/cli/src/cmd/init.rs +++ b/cli/src/cmd/init.rs @@ -16,6 +16,10 @@ pub async fn create_agentfs( if let Some(auth_token) = sync.auth_token { builder = builder.with_auth_token(auth_token); } + tracing::info!("partial_sync: {:?}", sync.partial_sync_experimental); + if let Some(partial_sync) = sync.partial_sync_experimental { + builder = builder.with_partial_sync_opts_experimental(partial_sync); + } let db = builder.build().await?; let conn = db.connect().await?; let agent = AgentFS::open_with(conn) diff --git a/cli/src/cmd/nfs.rs b/cli/src/cmd/nfs.rs index c34f41a3..b29a35d7 100644 --- a/cli/src/cmd/nfs.rs +++ b/cli/src/cmd/nfs.rs @@ -4,7 +4,7 @@ //! filesystem over the network, allowing remote systems (like VMs) to mount //! it as their root filesystem. -use agentfs_sdk::{agentfs_dir, AgentFS, AgentFSOptions, FileSystem, HostFS, OverlayFS}; +use agentfs_sdk::{agentfs_dir, AgentFSOptions, FileSystem, HostFS, OverlayFS}; use anyhow::{Context, Result}; use nfsserve::tcp::NFSTcp; use std::path::PathBuf; @@ -12,10 +12,16 @@ use std::sync::Arc; use tokio::signal; use tokio::sync::Mutex; +use crate::cmd::init::create_agentfs; use crate::nfs::AgentNFS; /// Handle the `nfs` command - start a standalone NFS server. -pub async fn handle_nfs_command(id_or_path: String, bind: String, port: u32) -> Result<()> { +pub async fn handle_nfs_command( + id_or_path: String, + sync_config_path: Option, + bind: String, + port: u32, +) -> Result<()> { // Resolve database path let db_path = resolve_db_path(&id_or_path)?; @@ -24,9 +30,8 @@ pub async fn handle_nfs_command(id_or_path: String, bind: String, port: u32) -> .to_str() .context("Database path contains non-UTF8 characters")?; - let agentfs = AgentFS::open(AgentFSOptions::with_path(db_path_str)) - .await - .context("Failed to open AgentFS database")?; + let options = AgentFSOptions::with_path(db_path_str); + let (_, agentfs) = create_agentfs(options, sync_config_path).await?; // Check if overlay is configured in the database let base_path = agentfs diff --git a/cli/src/fuse.rs b/cli/src/fuse.rs index fa434a5d..e2cea995 100644 --- a/cli/src/fuse.rs +++ b/cli/src/fuse.rs @@ -25,6 +25,47 @@ use tokio::runtime::Runtime; /// This is safe because we are the only writer to the filesystem. const TTL: Duration = Duration::MAX; +const SYNC_STATUS_INO: u64 = u64::MAX - 1; +const SYNC_STATUS_NAME: &str = ".fuse.sync.status"; + +const SYNC_CONTROL_INO: u64 = u64::MAX; +const SYNC_CONTROL_NAME: &str = ".fuse.sync.control"; + +const SYNC_FILE_INOS: &[u64] = &[SYNC_STATUS_INO, SYNC_CONTROL_INO]; + +fn sync_file_ino(name: &OsStr) -> Option { + match name.to_str() { + Some(SYNC_STATUS_NAME) => Some(SYNC_STATUS_INO), + Some(SYNC_CONTROL_NAME) => Some(SYNC_CONTROL_INO), + _ => None, + } +} + +fn sync_file_attr(ino: u64, uid: u32, gid: u32) -> FileAttr { + let (size, blocks, perm) = if ino == SYNC_CONTROL_INO { + (0, 0, 0o200) + } else { + (4096, 1, 0o400) + }; + FileAttr { + ino: ino, + size, + blocks, + atime: UNIX_EPOCH, + mtime: UNIX_EPOCH, + ctime: UNIX_EPOCH, + crtime: UNIX_EPOCH, + kind: FileType::RegularFile, + perm, + nlink: 1, + uid, + gid, + rdev: 0, + flags: 0, + blksize: 4096, + } +} + /// Options for mounting an agent filesystem via FUSE. #[derive(Debug, Clone)] pub struct FuseMountOptions { @@ -46,6 +87,8 @@ pub struct FuseMountOptions { enum OpenFile { /// The file handle from the filesystem layer. File { file: BoxedFile }, + /// Special .fuse.sync.control file + SyncControl, } struct AgentFSFuse { @@ -98,6 +141,12 @@ impl Filesystem for AgentFSFuse { /// resulting path, and caches the inode-to-path mapping on success. fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { tracing::info!("fuse::lookup(parent={parent}, name={name:?})"); + if self.synced_db.is_some() { + if let Some(ino) = sync_file_ino(name) { + reply.entry(&TTL, &&sync_file_attr(ino, self.uid, self.gid), 0); + return; + } + } let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -124,6 +173,10 @@ impl Filesystem for AgentFSFuse { /// directory identified by `ino`. Root inode (1) is handled specially. fn getattr(&mut self, _req: &Request, ino: u64, fh: Option, reply: ReplyAttr) { tracing::info!("fuse::getattr(ino={ino}, fh={fh:?})"); + if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { + reply.attr(&TTL, &&sync_file_attr(ino, self.uid, self.gid)); + return; + } let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -185,6 +238,10 @@ impl Filesystem for AgentFSFuse { reply: ReplyAttr, ) { tracing::info!("fuse::setattr(ino={ino}, size={size:?}, fh={fh:?})"); + if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { + reply.attr(&TTL, &&sync_file_attr(ino, self.uid, self.gid)); + return; + } // Handle truncate if let Some(new_size) = size { let result = if let Some(fh) = fh { @@ -627,6 +684,14 @@ impl Filesystem for AgentFSFuse { reply: ReplyCreate, ) { tracing::info!("fuse::create(parent={parent}, name={name:?}, mode={mode}, umask={umask}, flags={flags})"); + if self.synced_db.is_some() { + if let Some(ino) = sync_file_ino(name) { + let fh = self.alloc_fh(); + self.open_files.lock().insert(fh, OpenFile::SyncControl); + reply.created(&TTL, &&sync_file_attr(ino, self.uid, self.gid), 0, fh, 0); + return; + } + } let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -870,6 +935,12 @@ impl Filesystem for AgentFSFuse { /// Allocates a file handle and opens the file in the filesystem layer. fn open(&mut self, _req: &Request, ino: u64, flags: i32, reply: ReplyOpen) { tracing::info!("fuse::open(ino={ino}, flags={flags})"); + if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { + let fh = self.alloc_fh(); + self.open_files.lock().insert(fh, OpenFile::SyncControl); + reply.opened(fh, 0); + return; + } let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -904,6 +975,33 @@ impl Filesystem for AgentFSFuse { reply: ReplyData, ) { tracing::info!("fuse::read(ino={ino}, fh={fh:?}, offset={offset}, size={size}, flags={flags}, lock={lock:?})"); + if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { + tracing::info!("fuse::sync_control::read(offset={offset}, size={size})"); + let synced_db = self.synced_db.clone().unwrap(); + if ino == SYNC_STATUS_INO { + let stats = self + .runtime + .block_on(async move { synced_db.stats().await }); + match stats { + Ok(stats) => match serde_json::to_string(&stats) { + Ok(stats) => { + reply.data(stats.as_bytes()); + } + Err(err) => { + tracing::error!("sync-control stats serialization failed: {err}"); + reply.error(libc::EIO); + } + }, + Err(err) => { + tracing::error!("sync-control stats failed: {err}"); + reply.error(libc::EIO); + } + } + } else { + reply.data(&[]); + } + return; + } let file = { let open_files = self.open_files.lock(); let Some(OpenFile::File { file }) = open_files.get(&fh) else { @@ -937,6 +1035,33 @@ impl Filesystem for AgentFSFuse { reply: ReplyWrite, ) { tracing::info!("fuse::write(ino={ino}, fh={fh}, offset={offset}, data.len()={}, write_flags={write_flags}, flags={flags}, lock_owner={lock_owner:?})", data.len()); + if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { + tracing::info!("fuse::sync_control::write(offset={offset}, data={data:?})"); + let synced_db = self.synced_db.clone().unwrap(); + let command = std::str::from_utf8(data).map(|x| x.trim()); + if ino == SYNC_CONTROL_INO { + let result = match command { + Ok("push") => self.runtime.block_on(async move { synced_db.push().await }), + Ok("pull") => self + .runtime + .block_on(async move { synced_db.pull().await.map(|_| ()) }), + Ok("checkpoint") => self + .runtime + .block_on(async move { synced_db.checkpoint().await }), + _ => Err(turso::Error::Error("unexpected command".to_string())), + }; + match result { + Ok(()) => reply.written(data.len() as u32), + Err(err) => { + tracing::error!("sync-control command {command:?} failed: {err}"); + reply.error(libc::EIO); + } + } + } else { + reply.written(data.len() as u32); + } + return; + } let file = { let open_files = self.open_files.lock(); let Some(OpenFile::File { file }) = open_files.get(&fh) else { diff --git a/cli/src/main.rs b/cli/src/main.rs index dd168a3b..91e710e9 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -93,7 +93,7 @@ fn main() { sync_config_path, } => { let rt = get_runtime(); - if let Err(e) = rt.block_on(cmd::fs::diff_filesystem(id_or_path)) { + if let Err(e) = rt.block_on(cmd::fs::diff_filesystem(id_or_path, sync_config_path)) { eprintln!("Error: {}", e); std::process::exit(1); } @@ -109,6 +109,7 @@ fn main() { if let Err(e) = rt.block_on(cmd::fs::ls_filesystem( &mut std::io::stdout(), id_or_path, + sync_config_path, &fs_path, )) { eprintln!("Error: {}", e); @@ -119,6 +120,7 @@ fn main() { if let Err(e) = rt.block_on(cmd::fs::cat_filesystem( &mut std::io::stdout(), id_or_path, + sync_config_path, &file_path, )) { eprintln!("Error: {}", e); @@ -136,7 +138,12 @@ fn main() { port, } => { let rt = get_runtime(); - if let Err(e) = rt.block_on(cmd::nfs::handle_nfs_command(id_or_path, bind, port)) { + if let Err(e) = rt.block_on(cmd::nfs::handle_nfs_command( + id_or_path, + sync_config_path, + bind, + port, + )) { eprintln!("Error: {}", e); std::process::exit(1); } diff --git a/cli/src/parser.rs b/cli/src/parser.rs index 851cf040..cafc229d 100644 --- a/cli/src/parser.rs +++ b/cli/src/parser.rs @@ -6,6 +6,7 @@ use clap_complete::{ }; use serde::Deserialize; use std::path::{Path, PathBuf}; +use turso::sync::PartialSyncOpts; #[derive(Parser, Debug)] #[command(name = "agentfs")] @@ -20,6 +21,7 @@ pub struct Args { pub struct SyncConfig { pub remote_url: String, pub auth_token: Option, + pub partial_sync_experimental: Option, } impl SyncConfig { diff --git a/sandbox/Cargo.lock b/sandbox/Cargo.lock index fc83896b..81e79477 100644 --- a/sandbox/Cargo.lock +++ b/sandbox/Cargo.lock @@ -2652,7 +2652,7 @@ dependencies = [ [[package]] name = "turso" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "mimalloc", "thiserror 2.0.17", @@ -2665,7 +2665,7 @@ dependencies = [ [[package]] name = "turso_core" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "aegis", "aes", @@ -2717,7 +2717,7 @@ dependencies = [ [[package]] name = "turso_ext" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "chrono", "getrandom 0.3.4", @@ -2727,7 +2727,7 @@ dependencies = [ [[package]] name = "turso_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "proc-macro2", "quote", @@ -2737,7 +2737,7 @@ dependencies = [ [[package]] name = "turso_parser" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "bitflags 2.10.0", "miette", @@ -2750,7 +2750,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "bindgen", "env_logger", @@ -2764,7 +2764,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "proc-macro2", "quote", @@ -2774,7 +2774,7 @@ dependencies = [ [[package]] name = "turso_sync_engine" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "base64", "bytes", @@ -2795,7 +2795,7 @@ dependencies = [ [[package]] name = "turso_sync_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee#5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "bindgen", "env_logger", diff --git a/sdk/rust/Cargo.toml b/sdk/rust/Cargo.toml index 43f72cca..90eb7687 100644 --- a/sdk/rust/Cargo.toml +++ b/sdk/rust/Cargo.toml @@ -6,7 +6,7 @@ description = "AgentFS SDK for Rust" license = "MIT" [dependencies] -turso = { git = "https://github.com/tursodatabase/turso", rev = "5d1f07ac241286fc3cbf7f7c707abcd6df46d2ee" } +turso = { git = "https://github.com/tursodatabase/turso", rev = "bd2b3314a0d860ac8bde2fe560650f7d753591eb" } tokio = { version = "1", features = ["full"] } async-trait = "0.1" serde = { version = "1.0", features = ["derive"] } From 877a34b03de84795e1fe81f7b9e26e13a7fe822b Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Thu, 25 Dec 2025 12:40:43 +0400 Subject: [PATCH 07/11] small fix --- cli/src/fuse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/fuse.rs b/cli/src/fuse.rs index e2cea995..361aeacb 100644 --- a/cli/src/fuse.rs +++ b/cli/src/fuse.rs @@ -87,7 +87,7 @@ pub struct FuseMountOptions { enum OpenFile { /// The file handle from the filesystem layer. File { file: BoxedFile }, - /// Special .fuse.sync.control file + /// Special .fuse.sync.* files SyncControl, } From 2c9c3a0efc6e9b826d804758f96b9ae26fa47975 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Thu, 25 Dec 2025 12:42:23 +0400 Subject: [PATCH 08/11] fix clippy --- cli/src/fuse.rs | 10 +- sdk/rust/Cargo.lock | 478 +++++++++++++++++++++++++++++++++++++++++--- sdk/rust/src/lib.rs | 2 +- 3 files changed, 454 insertions(+), 36 deletions(-) diff --git a/cli/src/fuse.rs b/cli/src/fuse.rs index 361aeacb..0f1f31bd 100644 --- a/cli/src/fuse.rs +++ b/cli/src/fuse.rs @@ -48,7 +48,7 @@ fn sync_file_attr(ino: u64, uid: u32, gid: u32) -> FileAttr { (4096, 1, 0o400) }; FileAttr { - ino: ino, + ino, size, blocks, atime: UNIX_EPOCH, @@ -143,7 +143,7 @@ impl Filesystem for AgentFSFuse { tracing::info!("fuse::lookup(parent={parent}, name={name:?})"); if self.synced_db.is_some() { if let Some(ino) = sync_file_ino(name) { - reply.entry(&TTL, &&sync_file_attr(ino, self.uid, self.gid), 0); + reply.entry(&TTL, &sync_file_attr(ino, self.uid, self.gid), 0); return; } } @@ -174,7 +174,7 @@ impl Filesystem for AgentFSFuse { fn getattr(&mut self, _req: &Request, ino: u64, fh: Option, reply: ReplyAttr) { tracing::info!("fuse::getattr(ino={ino}, fh={fh:?})"); if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { - reply.attr(&TTL, &&sync_file_attr(ino, self.uid, self.gid)); + reply.attr(&TTL, &sync_file_attr(ino, self.uid, self.gid)); return; } let Some(path) = self.get_path(ino) else { @@ -239,7 +239,7 @@ impl Filesystem for AgentFSFuse { ) { tracing::info!("fuse::setattr(ino={ino}, size={size:?}, fh={fh:?})"); if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { - reply.attr(&TTL, &&sync_file_attr(ino, self.uid, self.gid)); + reply.attr(&TTL, &sync_file_attr(ino, self.uid, self.gid)); return; } // Handle truncate @@ -688,7 +688,7 @@ impl Filesystem for AgentFSFuse { if let Some(ino) = sync_file_ino(name) { let fh = self.alloc_fh(); self.open_files.lock().insert(fh, OpenFile::SyncControl); - reply.created(&TTL, &&sync_file_attr(ino, self.uid, self.gid), 0, fh, 0); + reply.created(&TTL, &sync_file_attr(ino, self.uid, self.gid), 0, fh, 0); return; } } diff --git a/sdk/rust/Cargo.lock b/sdk/rust/Cargo.lock index 36cbb96c..90b8f462 100644 --- a/sdk/rust/Cargo.lock +++ b/sdk/rust/Cargo.lock @@ -124,6 +124,35 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn", + "which", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -226,6 +255,15 @@ dependencies = [ "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -288,6 +326,17 @@ dependencies = [ "inout", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.53" @@ -375,6 +424,15 @@ dependencies = [ "itertools", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -436,6 +494,15 @@ dependencies = [ "cipher", ] +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -453,6 +520,25 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "env_filter", + "log", +] + [[package]] name = "errno" version = "0.3.14" @@ -557,6 +643,21 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "genawaiter" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0" +dependencies = [ + "genawaiter-macro", +] + +[[package]] +name = "genawaiter-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc" + [[package]] name = "generic-array" version = "0.14.7" @@ -613,6 +714,12 @@ dependencies = [ "url", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "half" version = "2.7.1" @@ -642,6 +749,25 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + [[package]] name = "iana-time-zone" version = "0.1.64" @@ -849,6 +975,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.178" @@ -905,6 +1037,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -932,6 +1070,15 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + [[package]] name = "memchr" version = "2.7.6" @@ -978,6 +1125,12 @@ dependencies = [ "libmimalloc-sys", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "mio" version = "1.1.1" @@ -989,6 +1142,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -998,6 +1161,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.19" @@ -1125,7 +1294,7 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix", + "rustix 1.1.3", "windows-sys 0.61.2", ] @@ -1150,6 +1319,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1159,6 +1334,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -1187,6 +1372,29 @@ dependencies = [ "unarray", ] +[[package]] +name = "prost" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -1332,12 +1540,31 @@ dependencies = [ "byteorder", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + [[package]] name = "rustix" version = "1.1.3" @@ -1347,7 +1574,7 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.11.0", "windows-sys 0.61.2", ] @@ -1559,7 +1786,7 @@ dependencies = [ "fastrand", "getrandom 0.3.4", "once_cell", - "rustix", + "rustix 1.1.3", "windows-sys 0.61.2", ] @@ -1612,6 +1839,37 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinystr" version = "0.8.2" @@ -1671,6 +1929,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +dependencies = [ + "crossbeam-channel", + "thiserror 2.0.17", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.31" @@ -1709,32 +1979,35 @@ version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ + "matchers", "nu-ansi-term", + "once_cell", + "regex-automata", "sharded-slab", "smallvec", "thread_local", + "tracing", "tracing-core", "tracing-log", ] [[package]] name = "turso" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f927be684ce9c612d2f11d04b9d48f4ba553fa98cf6e3bd2073c16602c8d7dc4" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "mimalloc", "thiserror 2.0.17", "tracing", "tracing-subscriber", - "turso_core", + "turso_sdk_kit", + "turso_sync_sdk_kit", ] [[package]] name = "turso_core" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88848a55b47fde014039f36dad09f33af34ad27ed94e70161fb8cf435ea3f3" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "aegis", "aes", @@ -1765,8 +2038,8 @@ dependencies = [ "regex", "regex-syntax", "roaring", - "rustc-hash", - "rustix", + "rustc-hash 2.1.1", + "rustix 1.1.3", "ryu", "simsimd", "strum", @@ -1785,9 +2058,8 @@ dependencies = [ [[package]] name = "turso_ext" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e03badf97da72c41c3289ac19245109096e6262017151dad23ecf7115ee433" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "chrono", "getrandom 0.3.4", @@ -1796,9 +2068,8 @@ dependencies = [ [[package]] name = "turso_macros" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681754e341a0420494203b73e067b0b69b29ccf0852d0d24bc7d5255d4441dea" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "proc-macro2", "quote", @@ -1807,9 +2078,8 @@ dependencies = [ [[package]] name = "turso_parser" -version = "0.4.0-pre.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92dd528809dfb32facb949b9b5416a33ee8f856b444f882854c0835428f511dd" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" dependencies = [ "bitflags", "miette", @@ -1819,6 +2089,69 @@ dependencies = [ "turso_macros", ] +[[package]] +name = "turso_sdk_kit" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +dependencies = [ + "bindgen", + "env_logger", + "tracing", + "tracing-appender", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit_macros", +] + +[[package]] +name = "turso_sdk_kit_macros" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "turso_sync_engine" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +dependencies = [ + "base64", + "bytes", + "genawaiter", + "http", + "libc", + "prost", + "roaring", + "serde", + "serde_json", + "thiserror 2.0.17", + "tracing", + "turso_core", + "turso_parser", + "uuid", +] + +[[package]] +name = "turso_sync_sdk_kit" +version = "0.4.0-pre.18" +source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +dependencies = [ + "bindgen", + "env_logger", + "genawaiter", + "parking_lot", + "tracing", + "tracing-appender", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit", + "turso_sdk_kit_macros", + "turso_sync_engine", +] + [[package]] name = "twox-hash" version = "2.1.2" @@ -2008,6 +2341,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "winapi-util" version = "0.1.11" @@ -2076,13 +2421,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets", + "windows-targets 0.53.5", ] [[package]] @@ -2094,6 +2448,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + [[package]] name = "windows-targets" version = "0.53.5" @@ -2101,58 +2471,106 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_aarch64_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + [[package]] name = "windows_i686_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_i686_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "windows_x86_64_msvc" version = "0.53.1" diff --git a/sdk/rust/src/lib.rs b/sdk/rust/src/lib.rs index a5aad86c..31d886f9 100644 --- a/sdk/rust/src/lib.rs +++ b/sdk/rust/src/lib.rs @@ -53,7 +53,7 @@ impl AgentFSOptions { Ok(path.to_string()) } else if let Some(id) = &self.id { // Validate agent ID to prevent path traversal attacks - if !Self::validate_agent_id(&id) { + if !Self::validate_agent_id(id) { anyhow::bail!( "Invalid agent ID '{}'. Agent IDs must contain only alphanumeric characters, hyphens, and underscores.", id From e701fc6c263da05d2c5376887c5c05a4e7d13cff Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Thu, 25 Dec 2025 12:51:24 +0400 Subject: [PATCH 09/11] fix compilation --- cli/src/sandbox/overlay.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/sandbox/overlay.rs b/cli/src/sandbox/overlay.rs index 997b33e0..7cdb6e34 100644 --- a/cli/src/sandbox/overlay.rs +++ b/cli/src/sandbox/overlay.rs @@ -117,7 +117,7 @@ pub async fn run_cmd( // Start FUSE in a separate thread let fuse_handle = std::thread::spawn(move || { let rt = crate::get_runtime(); - crate::fuse::mount(overlay, fuse_opts, rt) + crate::fuse::mount(overlay, None, fuse_opts, rt) }); // Wait for FUSE mount to be ready From 5a364546faaff4ce4d1746bfba518ddc9c743289 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Fri, 26 Dec 2025 21:43:39 +0400 Subject: [PATCH 10/11] wip --- cli/Cargo.lock | 19 +-- cli/Cargo.toml | 2 +- cli/src/cmd/fs.rs | 33 ++--- cli/src/cmd/init.rs | 68 ++++++++-- cli/src/cmd/mod.rs | 1 + cli/src/cmd/mount.rs | 8 +- cli/src/cmd/nfs.rs | 11 +- cli/src/cmd/sync.rs | 59 +++++++++ cli/src/fuse.rs | 245 +++++++------------------------------ cli/src/main.rs | 63 ++++++---- cli/src/parser.rs | 66 +++++----- cli/src/sandbox/overlay.rs | 2 +- sandbox/Cargo.lock | 19 +-- sdk/rust/Cargo.toml | 2 +- 14 files changed, 279 insertions(+), 319 deletions(-) create mode 100644 cli/src/cmd/sync.rs diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 731d8b81..5d97a3ec 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -3128,7 +3128,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "turso" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "bytes", "http-body-util", @@ -3147,7 +3147,7 @@ dependencies = [ [[package]] name = "turso_core" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "aegis", "aes", @@ -3199,7 +3199,7 @@ dependencies = [ [[package]] name = "turso_ext" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "chrono", "getrandom 0.3.4", @@ -3209,7 +3209,7 @@ dependencies = [ [[package]] name = "turso_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "proc-macro2", "quote", @@ -3219,9 +3219,10 @@ dependencies = [ [[package]] name = "turso_parser" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "bitflags 2.10.0", + "memchr", "miette", "strum", "strum_macros", @@ -3232,7 +3233,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "bindgen", "env_logger", @@ -3246,7 +3247,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "proc-macro2", "quote", @@ -3256,7 +3257,7 @@ dependencies = [ [[package]] name = "turso_sync_engine" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "base64", "bytes", @@ -3277,7 +3278,7 @@ dependencies = [ [[package]] name = "turso_sync_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "bindgen", "env_logger", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 705814b4..e0bdc4f1 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -17,7 +17,7 @@ agentfs-sdk = { path = "../sdk/rust" } tokio = { version = "1", features = ["full"] } clap = { version = "4", features = ["derive"] } anyhow = "1.0" -turso = { git = "https://github.com/tursodatabase/turso", rev = "bd2b3314a0d860ac8bde2fe560650f7d753591eb", features = [ +turso = { git = "https://github.com/tursodatabase/turso", rev = "00b0890c8fef7f1ca58ed24da2c1a872816e2c1a", features = [ "sync", ] } serde = { version = "1.0", features = ["derive"] } diff --git a/cli/src/cmd/fs.rs b/cli/src/cmd/fs.rs index a46aac7f..c0e8eb91 100644 --- a/cli/src/cmd/fs.rs +++ b/cli/src/cmd/fs.rs @@ -4,7 +4,7 @@ use agentfs_sdk::AgentFSOptions; use anyhow::{Context, Result as AnyhowResult}; use turso::Value; -use crate::cmd::init::create_agentfs; +use crate::cmd::init::open_agentfs; const ROOT_INO: i64 = 1; const S_IFMT: u32 = 0o170000; @@ -15,13 +15,12 @@ const S_IFLNK: u32 = 0o120000; pub async fn ls_filesystem( stdout: &mut impl std::io::Write, id_or_path: String, - sync_config_path: Option, path: &str, ) -> AnyhowResult<()> { let options = AgentFSOptions::resolve(&id_or_path)?; eprintln!("Using agent: {}", id_or_path); - let (_, agentfs) = create_agentfs(options, sync_config_path).await?; + let (_, agentfs) = open_agentfs(options).await?; let conn = agentfs.get_connection(); if path != "/" { @@ -99,11 +98,10 @@ pub async fn ls_filesystem( pub async fn cat_filesystem( stdout: &mut impl std::io::Write, id_or_path: String, - sync_config_path: Option, path: &str, ) -> AnyhowResult<()> { let options = AgentFSOptions::resolve(&id_or_path)?; - let (_, agentfs) = create_agentfs(options, sync_config_path).await?; + let (_, agentfs) = open_agentfs(options).await?; let conn = agentfs.get_connection(); let path_components: Vec<&str> = path @@ -225,14 +223,11 @@ fn path_exists_in_base(base_path: &str, rel_path: &str) -> bool { std::path::Path::new(&full_path).exists() } -pub async fn diff_filesystem( - id_or_path: String, - sync_config_path: Option, -) -> AnyhowResult<()> { +pub async fn diff_filesystem(id_or_path: String) -> AnyhowResult<()> { let options = AgentFSOptions::resolve(&id_or_path)?; eprintln!("Using agent: {}", id_or_path); - let (_, agent) = create_agentfs(options, sync_config_path) + let (_, agent) = open_agentfs(options) .await .context("Failed to open agent")?; @@ -324,9 +319,7 @@ mod tests { pub async fn cat_file_not_found() { let (_agentfs, path, _file) = agentfs().await; let mut buf = Vec::new(); - let err = cat_filesystem(&mut buf, path, None, "test.md") - .await - .unwrap_err(); + let err = cat_filesystem(&mut buf, path, "test.md").await.unwrap_err(); assert!(err.to_string().contains("File not found")); } @@ -336,9 +329,7 @@ mod tests { let content = b"hello, agentfs"; agentfs.fs.write_file("test.md", content).await.unwrap(); let mut buf = Vec::new(); - cat_filesystem(&mut buf, path, None, "test.md") - .await - .unwrap(); + cat_filesystem(&mut buf, path, "test.md").await.unwrap(); assert_eq!(buf, content); } @@ -348,9 +339,7 @@ mod tests { let content = vec![100u8; 4 * 1024 * 1024]; agentfs.fs.write_file("test.md", &content).await.unwrap(); let mut buf = Vec::new(); - cat_filesystem(&mut buf, path, None, "test.md") - .await - .unwrap(); + cat_filesystem(&mut buf, path, "test.md").await.unwrap(); assert_eq!(buf, content); } @@ -358,7 +347,7 @@ mod tests { pub async fn ls_empty() { let (_agentfs, path, _file) = agentfs().await; let mut buf = Vec::new(); - ls_filesystem(&mut buf, path, None, "/").await.unwrap(); + ls_filesystem(&mut buf, path, "/").await.unwrap(); assert_eq!(buf, b""); } @@ -370,7 +359,7 @@ mod tests { let big = vec![100u8; 1024 * 1024]; agentfs.fs.write_file("3.md", &big).await.unwrap(); let mut buf = Vec::new(); - ls_filesystem(&mut buf, path, None, "/").await.unwrap(); + ls_filesystem(&mut buf, path, "/").await.unwrap(); assert_eq!( buf, b"f 1.md @@ -393,7 +382,7 @@ f 3.md let big = vec![100u8; 1024 * 1024]; agentfs.fs.write_file("d/e/3.md", &big).await.unwrap(); let mut buf = Vec::new(); - ls_filesystem(&mut buf, path, None, "/").await.unwrap(); + ls_filesystem(&mut buf, path, "/").await.unwrap(); assert_eq!( buf, b"d a diff --git a/cli/src/cmd/init.rs b/cli/src/cmd/init.rs index 8803ff4e..bcf3384b 100644 --- a/cli/src/cmd/init.rs +++ b/cli/src/cmd/init.rs @@ -3,21 +3,69 @@ use std::time::{SystemTime, UNIX_EPOCH}; use agentfs_sdk::{agentfs_dir, AgentFS, AgentFSOptions, OverlayFS}; use anyhow::{Context, Result as AnyhowResult}; +use turso::sync::{PartialBootstrapStrategy, PartialSyncOpts}; -use crate::parser::SyncConfig; +use crate::parser::SyncCommandOptions; + +pub async fn open_agentfs( + options: AgentFSOptions, +) -> anyhow::Result<(Option, AgentFS)> { + let path = options.db_path()?; + let meta_path = format!("{path}-info"); + if !std::fs::exists(meta_path)? { + return Ok(( + None, + AgentFS::open(options) + .await + .context("Failed to open database")?, + )); + } + let mut builder = turso::sync::Builder::new_remote(&options.db_path()?); + if let Ok(auth_token) = std::env::var("TURSO_DB_AUTH_TOKEN") { + builder = builder.with_auth_token(auth_token); + } + let db = builder.build().await?; + let conn = db.connect().await?; + let agent = AgentFS::open_with(conn) + .await + .context("Failed to open synced database")?; + Ok((Some(db), agent)) +} pub async fn create_agentfs( options: AgentFSOptions, - sync_config_path: Option, + sync_options: SyncCommandOptions, ) -> anyhow::Result<(Option, AgentFS)> { - let sync_config = SyncConfig::parse(sync_config_path)?; - if let Some(sync) = sync_config { - let mut builder = turso::sync::Builder::new_remote(&options.db_path()?, &sync.remote_url); - if let Some(auth_token) = sync.auth_token { + if let Some(remote_url) = sync_options.sync_remote_url { + let mut builder = + turso::sync::Builder::new_remote(&options.db_path()?).with_remote_url(remote_url); + if let Ok(auth_token) = std::env::var("TURSO_DB_AUTH_TOKEN") { builder = builder.with_auth_token(auth_token); } - tracing::info!("partial_sync: {:?}", sync.partial_sync_experimental); - if let Some(partial_sync) = sync.partial_sync_experimental { + let mut partial_sync = PartialSyncOpts { + bootstrap_strategy: Some(PartialBootstrapStrategy::Prefix { length: 128 * 1024 }), + prefetch: false, + segment_size: 128 * 1024, + }; + let mut has_partial_sync = false; + if let Some(prefetch) = sync_options.sync_partial_prefetch { + partial_sync.prefetch = prefetch; + has_partial_sync = true; + } + if let Some(segment_size) = sync_options.sync_partial_segment_size { + partial_sync.segment_size = segment_size; + has_partial_sync = true; + } + if let Some(length) = sync_options.sync_partial_bootstrap_length { + partial_sync.bootstrap_strategy = + Some(PartialBootstrapStrategy::Prefix { length: length }); + has_partial_sync = true; + } + if let Some(query) = sync_options.sync_partial_bootstrap_query { + partial_sync.bootstrap_strategy = Some(PartialBootstrapStrategy::Query { query }); + has_partial_sync = true; + } + if has_partial_sync { builder = builder.with_partial_sync_opts_experimental(partial_sync); } let db = builder.build().await?; @@ -38,7 +86,7 @@ pub async fn create_agentfs( pub async fn init_database( id: Option, - sync_config_path: Option, + sync_options: SyncCommandOptions, force: bool, base: Option, ) -> AnyhowResult<()> { @@ -93,7 +141,7 @@ pub async fn init_database( // Use the SDK to initialize the database - this ensures consistency // The SDK will create .agentfs directory and database file let options = AgentFSOptions::with_id(&id); - let (synced_db, agent) = create_agentfs(options, sync_config_path).await?; + let (synced_db, agent) = create_agentfs(options, sync_options).await?; // If base is provided, initialize the overlay schema using the SDK if let Some(base_path) = base { diff --git a/cli/src/cmd/mod.rs b/cli/src/cmd/mod.rs index c02927b5..d201e07c 100644 --- a/cli/src/cmd/mod.rs +++ b/cli/src/cmd/mod.rs @@ -1,6 +1,7 @@ pub mod completions; pub mod fs; pub mod init; +pub mod sync; #[cfg(any(target_os = "linux", target_os = "macos"))] mod mount; diff --git a/cli/src/cmd/mount.rs b/cli/src/cmd/mount.rs index 9403a39b..9dd9e0e4 100644 --- a/cli/src/cmd/mount.rs +++ b/cli/src/cmd/mount.rs @@ -3,15 +3,13 @@ use anyhow::Result; use std::{os::unix::fs::MetadataExt, path::PathBuf, sync::Arc}; use turso::value::Value; -use crate::{cmd::init::create_agentfs, fuse::FuseMountOptions}; +use crate::{cmd::init::open_agentfs, fuse::FuseMountOptions}; /// Arguments for the mount command. #[derive(Debug, Clone)] pub struct MountArgs { /// The agent filesystem ID or path. pub id_or_path: String, - /// optional sync config path - pub sync_config_path: Option, /// The mountpoint path. pub mountpoint: PathBuf, /// Automatically unmount when the process exits. @@ -65,7 +63,7 @@ pub fn mount(args: MountArgs) -> Result<()> { let mount = move || { let rt = crate::get_runtime(); - let (db, agentfs) = rt.block_on(create_agentfs(opts, args.sync_config_path))?; + let (_db, agentfs) = rt.block_on(open_agentfs(opts))?; // Check for overlay configuration let fs: Arc = rt.block_on(async { @@ -102,7 +100,7 @@ pub fn mount(args: MountArgs) -> Result<()> { } })?; - crate::fuse::mount(fs, db, fuse_opts, rt) + crate::fuse::mount(fs, fuse_opts, rt) }; if args.foreground { diff --git a/cli/src/cmd/nfs.rs b/cli/src/cmd/nfs.rs index b29a35d7..c390fe29 100644 --- a/cli/src/cmd/nfs.rs +++ b/cli/src/cmd/nfs.rs @@ -12,16 +12,11 @@ use std::sync::Arc; use tokio::signal; use tokio::sync::Mutex; -use crate::cmd::init::create_agentfs; +use crate::cmd::init::open_agentfs; use crate::nfs::AgentNFS; /// Handle the `nfs` command - start a standalone NFS server. -pub async fn handle_nfs_command( - id_or_path: String, - sync_config_path: Option, - bind: String, - port: u32, -) -> Result<()> { +pub async fn handle_nfs_command(id_or_path: String, bind: String, port: u32) -> Result<()> { // Resolve database path let db_path = resolve_db_path(&id_or_path)?; @@ -31,7 +26,7 @@ pub async fn handle_nfs_command( .context("Database path contains non-UTF8 characters")?; let options = AgentFSOptions::with_path(db_path_str); - let (_, agentfs) = create_agentfs(options, sync_config_path).await?; + let (_, agentfs) = open_agentfs(options).await?; // Check if overlay is configured in the database let base_path = agentfs diff --git a/cli/src/cmd/sync.rs b/cli/src/cmd/sync.rs new file mode 100644 index 00000000..24aee6c7 --- /dev/null +++ b/cli/src/cmd/sync.rs @@ -0,0 +1,59 @@ +use agentfs_sdk::AgentFSOptions; +use anyhow::anyhow; + +use crate::cmd::init::open_agentfs; + +pub async fn handle_pull_command(id_or_path: String) -> anyhow::Result<()> { + let options = AgentFSOptions::resolve(&id_or_path)?; + eprintln!("Using agent: {}", id_or_path); + + let (db, _) = open_agentfs(options).await?; + let Some(db) = db else { + return Err(anyhow!("db is not connected to the remote")); + }; + db.pull().await?; + eprintln!("Remote data pulled to local db successfully"); + Ok(()) +} + +pub async fn handle_push_command(id_or_path: String) -> anyhow::Result<()> { + let options = AgentFSOptions::resolve(&id_or_path)?; + eprintln!("Using agent: {}", id_or_path); + + let (db, _) = open_agentfs(options).await?; + let Some(db) = db else { + return Err(anyhow!("db is not connected to the remote")); + }; + db.push().await?; + eprintln!("Local data pushed to remote db successfully"); + Ok(()) +} + +pub async fn handle_checkpoint_command(id_or_path: String) -> anyhow::Result<()> { + let options = AgentFSOptions::resolve(&id_or_path)?; + eprintln!("Using agent: {}", id_or_path); + + let (db, _) = open_agentfs(options).await?; + let Some(db) = db else { + return Err(anyhow!("db is not connected to the remote")); + }; + db.checkpoint().await?; + eprintln!("Local db checkpoined successfully"); + Ok(()) +} + +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)?; + eprintln!("Using agent: {}", id_or_path); + + let (db, _) = open_agentfs(options).await?; + let Some(db) = db else { + return Err(anyhow!("db is not connected to the remote")); + }; + let stats = db.stats().await?; + stdout.write(serde_json::to_string(&stats)?.as_bytes())?; + Ok(()) +} diff --git a/cli/src/fuse.rs b/cli/src/fuse.rs index 0f1f31bd..22e2dd5c 100644 --- a/cli/src/fuse.rs +++ b/cli/src/fuse.rs @@ -25,47 +25,6 @@ use tokio::runtime::Runtime; /// This is safe because we are the only writer to the filesystem. const TTL: Duration = Duration::MAX; -const SYNC_STATUS_INO: u64 = u64::MAX - 1; -const SYNC_STATUS_NAME: &str = ".fuse.sync.status"; - -const SYNC_CONTROL_INO: u64 = u64::MAX; -const SYNC_CONTROL_NAME: &str = ".fuse.sync.control"; - -const SYNC_FILE_INOS: &[u64] = &[SYNC_STATUS_INO, SYNC_CONTROL_INO]; - -fn sync_file_ino(name: &OsStr) -> Option { - match name.to_str() { - Some(SYNC_STATUS_NAME) => Some(SYNC_STATUS_INO), - Some(SYNC_CONTROL_NAME) => Some(SYNC_CONTROL_INO), - _ => None, - } -} - -fn sync_file_attr(ino: u64, uid: u32, gid: u32) -> FileAttr { - let (size, blocks, perm) = if ino == SYNC_CONTROL_INO { - (0, 0, 0o200) - } else { - (4096, 1, 0o400) - }; - FileAttr { - ino, - size, - blocks, - atime: UNIX_EPOCH, - mtime: UNIX_EPOCH, - ctime: UNIX_EPOCH, - crtime: UNIX_EPOCH, - kind: FileType::RegularFile, - perm, - nlink: 1, - uid, - gid, - rdev: 0, - flags: 0, - blksize: 4096, - } -} - /// Options for mounting an agent filesystem via FUSE. #[derive(Debug, Clone)] pub struct FuseMountOptions { @@ -84,16 +43,13 @@ pub struct FuseMountOptions { } /// Tracks an open file handle -enum OpenFile { +struct OpenFile { /// The file handle from the filesystem layer. - File { file: BoxedFile }, - /// Special .fuse.sync.* files - SyncControl, + file: BoxedFile, } struct AgentFSFuse { fs: Arc, - synced_db: Option, runtime: Runtime, path_cache: Arc>>, /// Maps file handle -> open file state @@ -120,7 +76,6 @@ impl Filesystem for AgentFSFuse { /// - No opendir support: skips opendir/releasedir calls since we don't track /// directory handles, reducing round-trips for directory operations. fn init(&mut self, _req: &Request, config: &mut KernelConfig) -> Result<(), libc::c_int> { - tracing::info!("fuse::init(config={config:?})"); let _ = config.add_capabilities( FUSE_ASYNC_READ | FUSE_WRITEBACK_CACHE @@ -140,13 +95,6 @@ impl Filesystem for AgentFSFuse { /// Resolves `name` under the directory identified by `parent` inode, stats the /// resulting path, and caches the inode-to-path mapping on success. fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { - tracing::info!("fuse::lookup(parent={parent}, name={name:?})"); - if self.synced_db.is_some() { - if let Some(ino) = sync_file_ino(name) { - reply.entry(&TTL, &sync_file_attr(ino, self.uid, self.gid), 0); - return; - } - } let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -171,12 +119,7 @@ impl Filesystem for AgentFSFuse { /// /// Returns metadata (size, permissions, timestamps, etc.) for the file or /// directory identified by `ino`. Root inode (1) is handled specially. - fn getattr(&mut self, _req: &Request, ino: u64, fh: Option, reply: ReplyAttr) { - tracing::info!("fuse::getattr(ino={ino}, fh={fh:?})"); - if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { - reply.attr(&TTL, &sync_file_attr(ino, self.uid, self.gid)); - return; - } + fn getattr(&mut self, _req: &Request, ino: u64, _fh: Option, reply: ReplyAttr) { let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -197,7 +140,6 @@ impl Filesystem for AgentFSFuse { /// Returns the path that the symlink points to. This is called by operations /// like `ls -l` to display symlink targets. fn readlink(&mut self, _req: &Request, ino: u64, reply: ReplyData) { - tracing::info!("fuse::readlink(ino={ino})"); let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -237,28 +179,22 @@ impl Filesystem for AgentFSFuse { _flags: Option, reply: ReplyAttr, ) { - tracing::info!("fuse::setattr(ino={ino}, size={size:?}, fh={fh:?})"); - if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { - reply.attr(&TTL, &sync_file_attr(ino, self.uid, self.gid)); - return; - } // Handle truncate if let Some(new_size) = size { let result = if let Some(fh) = fh { // Use file handle if available (ftruncate) let file = { let open_files = self.open_files.lock(); - match open_files.get(&fh) { - Some(OpenFile::File { file }) => file.clone(), - _ => { - reply.error(libc::EBADF); - return; - } - } + open_files.get(&fh).map(|f| f.file.clone()) }; - self.runtime - .block_on(async move { file.truncate(new_size).await }) + if let Some(file) = file { + self.runtime + .block_on(async move { file.truncate(new_size).await }) + } else { + reply.error(libc::EBADF); + return; + } } else { // Open file and truncate via file handle let Some(path) = self.path_cache.lock().get(&ino).cloned() else { @@ -310,11 +246,10 @@ impl Filesystem for AgentFSFuse { &mut self, _req: &Request, ino: u64, - fh: u64, + _fh: u64, offset: i64, mut reply: ReplyDirectory, ) { - tracing::info!("fuse::readdir(ino={ino}, fh={fh:?}, offset={offset})"); let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -410,11 +345,10 @@ impl Filesystem for AgentFSFuse { &mut self, _req: &Request, ino: u64, - fh: u64, + _fh: u64, offset: i64, mut reply: ReplyDirectoryPlus, ) { - tracing::info!("fuse::readdirplus(ino={ino}, fh={fh:?}, offset={offset})"); let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -552,11 +486,10 @@ impl Filesystem for AgentFSFuse { _req: &Request, parent: u64, name: &OsStr, - mode: u32, - umask: u32, + _mode: u32, + _umask: u32, reply: ReplyEntry, ) { - tracing::info!("fuse::mkdir(parent={parent}, name={name:?}, mode={mode}, mask={umask})"); let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -598,7 +531,6 @@ impl Filesystem for AgentFSFuse { /// Verifies the target is a directory and is empty before removal. /// Returns `ENOTDIR` if not a directory, `ENOTEMPTY` if not empty. fn rmdir(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { - tracing::info!("fuse::rmdir(parent={parent}, name={name:?})"); let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -678,20 +610,11 @@ impl Filesystem for AgentFSFuse { _req: &Request, parent: u64, name: &OsStr, - mode: u32, - umask: u32, - flags: i32, + _mode: u32, + _umask: u32, + _flags: i32, reply: ReplyCreate, ) { - tracing::info!("fuse::create(parent={parent}, name={name:?}, mode={mode}, umask={umask}, flags={flags})"); - if self.synced_db.is_some() { - if let Some(ino) = sync_file_ino(name) { - let fh = self.alloc_fh(); - self.open_files.lock().insert(fh, OpenFile::SyncControl); - reply.created(&TTL, &sync_file_attr(ino, self.uid, self.gid), 0, fh, 0); - return; - } - } let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -745,7 +668,7 @@ impl Filesystem for AgentFSFuse { }; let fh = self.alloc_fh(); - self.open_files.lock().insert(fh, OpenFile::File { file }); + self.open_files.lock().insert(fh, OpenFile { file }); reply.created(&TTL, &attr, 0, fh, 0); } @@ -761,9 +684,6 @@ impl Filesystem for AgentFSFuse { target: &Path, reply: ReplyEntry, ) { - tracing::info!( - "fuse::symlink(parent={parent}, link_name={link_name:?}, target={target:?})" - ); let Some(path) = self.lookup_path(parent, link_name) else { reply.error(libc::ENOENT); return; @@ -809,7 +729,6 @@ impl Filesystem for AgentFSFuse { /// /// Gets the file's inode before removal to clean up the path cache. fn unlink(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { - tracing::info!("fuse::unlink(parent={parent}, name={name:?})"); let Some(path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -864,10 +783,9 @@ impl Filesystem for AgentFSFuse { name: &OsStr, newparent: u64, newname: &OsStr, - flags: u32, + _flags: u32, reply: ReplyEmpty, ) { - tracing::info!("fuse::rename(parent={parent}, name={name:?}, newparent={newparent}, newname={newname:?}, flags={flags})"); let Some(from_path) = self.lookup_path(parent, name) else { reply.error(libc::ENOENT); return; @@ -933,14 +851,7 @@ impl Filesystem for AgentFSFuse { /// Opens a file for reading or writing. /// /// Allocates a file handle and opens the file in the filesystem layer. - fn open(&mut self, _req: &Request, ino: u64, flags: i32, reply: ReplyOpen) { - tracing::info!("fuse::open(ino={ino}, flags={flags})"); - if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { - let fh = self.alloc_fh(); - self.open_files.lock().insert(fh, OpenFile::SyncControl); - reply.opened(fh, 0); - return; - } + fn open(&mut self, _req: &Request, ino: u64, _flags: i32, reply: ReplyOpen) { let Some(path) = self.get_path(ino) else { reply.error(libc::ENOENT); return; @@ -955,7 +866,7 @@ impl Filesystem for AgentFSFuse { match result { Ok(file) => { let fh = self.alloc_fh(); - self.open_files.lock().insert(fh, OpenFile::File { file }); + self.open_files.lock().insert(fh, OpenFile { file }); reply.opened(fh, 0); } Err(_) => reply.error(libc::EIO), @@ -966,49 +877,21 @@ impl Filesystem for AgentFSFuse { fn read( &mut self, _req: &Request, - ino: u64, + _ino: u64, fh: u64, offset: i64, size: u32, - flags: i32, - lock: Option, + _flags: i32, + _lock: Option, reply: ReplyData, ) { - tracing::info!("fuse::read(ino={ino}, fh={fh:?}, offset={offset}, size={size}, flags={flags}, lock={lock:?})"); - if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { - tracing::info!("fuse::sync_control::read(offset={offset}, size={size})"); - let synced_db = self.synced_db.clone().unwrap(); - if ino == SYNC_STATUS_INO { - let stats = self - .runtime - .block_on(async move { synced_db.stats().await }); - match stats { - Ok(stats) => match serde_json::to_string(&stats) { - Ok(stats) => { - reply.data(stats.as_bytes()); - } - Err(err) => { - tracing::error!("sync-control stats serialization failed: {err}"); - reply.error(libc::EIO); - } - }, - Err(err) => { - tracing::error!("sync-control stats failed: {err}"); - reply.error(libc::EIO); - } - } - } else { - reply.data(&[]); - } - return; - } let file = { let open_files = self.open_files.lock(); - let Some(OpenFile::File { file }) = open_files.get(&fh) else { + let Some(open_file) = open_files.get(&fh) else { reply.error(libc::EBADF); return; }; - file.clone() + open_file.file.clone() }; let result = self @@ -1025,50 +908,22 @@ impl Filesystem for AgentFSFuse { fn write( &mut self, _req: &Request, - ino: u64, + _ino: u64, fh: u64, offset: i64, data: &[u8], - write_flags: u32, - flags: i32, - lock_owner: Option, + _write_flags: u32, + _flags: i32, + _lock_owner: Option, reply: ReplyWrite, ) { - tracing::info!("fuse::write(ino={ino}, fh={fh}, offset={offset}, data.len()={}, write_flags={write_flags}, flags={flags}, lock_owner={lock_owner:?})", data.len()); - if self.synced_db.is_some() && SYNC_FILE_INOS.contains(&ino) { - tracing::info!("fuse::sync_control::write(offset={offset}, data={data:?})"); - let synced_db = self.synced_db.clone().unwrap(); - let command = std::str::from_utf8(data).map(|x| x.trim()); - if ino == SYNC_CONTROL_INO { - let result = match command { - Ok("push") => self.runtime.block_on(async move { synced_db.push().await }), - Ok("pull") => self - .runtime - .block_on(async move { synced_db.pull().await.map(|_| ()) }), - Ok("checkpoint") => self - .runtime - .block_on(async move { synced_db.checkpoint().await }), - _ => Err(turso::Error::Error("unexpected command".to_string())), - }; - match result { - Ok(()) => reply.written(data.len() as u32), - Err(err) => { - tracing::error!("sync-control command {command:?} failed: {err}"); - reply.error(libc::EIO); - } - } - } else { - reply.written(data.len() as u32); - } - return; - } let file = { let open_files = self.open_files.lock(); - let Some(OpenFile::File { file }) = open_files.get(&fh) else { + let Some(open_file) = open_files.get(&fh) else { reply.error(libc::EBADF); return; }; - file.clone() + open_file.file.clone() }; let data_len = data.len(); @@ -1086,8 +941,7 @@ impl Filesystem for AgentFSFuse { /// Flushes data to the backend storage. /// /// Since writes go directly to the database, this is a no-op. - fn flush(&mut self, _req: &Request, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) { - tracing::info!("fuse::flush(ino={ino}, fh={fh}, lock_owner={lock_owner})"); + fn flush(&mut self, _req: &Request, _ino: u64, fh: u64, _lock_owner: u64, reply: ReplyEmpty) { let open_files = self.open_files.lock(); if open_files.contains_key(&fh) { reply.ok(); @@ -1100,13 +954,12 @@ impl Filesystem for AgentFSFuse { /// /// This now uses the file handle's fsync which knows which layer(s) the /// file exists in, avoiding errors when a file only exists in one layer. - fn fsync(&mut self, _req: &Request, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) { - tracing::info!("fuse::fsync(ino={ino}, fh={fh}, datasync={datasync})"); + fn fsync(&mut self, _req: &Request, _ino: u64, fh: u64, _datasync: bool, reply: ReplyEmpty) { let file = { let open_files = self.open_files.lock(); match open_files.get(&fh) { - Some(OpenFile::File { file }) => file.clone(), - _ => { + Some(open_file) => open_file.file.clone(), + None => { reply.error(libc::EBADF); return; } @@ -1128,14 +981,13 @@ impl Filesystem for AgentFSFuse { fn release( &mut self, _req: &Request, - ino: u64, + _ino: u64, fh: u64, - flags: i32, - lock_owner: Option, - flush: bool, + _flags: i32, + _lock_owner: Option, + _flush: bool, reply: ReplyEmpty, ) { - tracing::info!("fuse::release(ino={ino}, fh={fh}, flags={flags}, lock_owner={lock_owner:?}, flush={flush})"); self.open_files.lock().remove(&fh); reply.ok(); } @@ -1143,8 +995,7 @@ impl Filesystem for AgentFSFuse { /// Returns filesystem statistics. /// /// Queries actual usage from the SDK and reports it to tools like `df`. - fn statfs(&mut self, _req: &Request, ino: u64, reply: ReplyStatfs) { - tracing::info!("fuse::statfs(ino={ino})"); + fn statfs(&mut self, _req: &Request, _ino: u64, reply: ReplyStatfs) { const BLOCK_SIZE: u64 = 4096; const TOTAL_INODES: u64 = 1_000_000; // Virtual limit const MAX_NAMELEN: u32 = 255; @@ -1186,16 +1037,9 @@ impl AgentFSFuse { /// /// The uid and gid are used for all file ownership to avoid "dubious ownership" /// errors from tools like git that check file ownership. - fn new( - fs: Arc, - synced_db: Option, - runtime: Runtime, - uid: u32, - gid: u32, - ) -> Self { + fn new(fs: Arc, runtime: Runtime, uid: u32, gid: u32) -> Self { Self { fs, - synced_db, runtime, path_cache: Arc::new(Mutex::new(HashMap::new())), open_files: Arc::new(Mutex::new(HashMap::new())), @@ -1303,7 +1147,6 @@ fn fillattr(stats: &Stats, uid: u32, gid: u32) -> FileAttr { pub fn mount( fs: Arc, - synced_db: Option, opts: FuseMountOptions, runtime: Runtime, ) -> anyhow::Result<()> { @@ -1312,7 +1155,7 @@ pub fn mount( let uid = opts.uid.unwrap_or_else(|| unsafe { libc::getuid() }); let gid = opts.gid.unwrap_or_else(|| unsafe { libc::getgid() }); - let fs = AgentFSFuse::new(fs, synced_db, runtime, uid, gid); + let fs = AgentFSFuse::new(fs, runtime, uid, gid); fs.add_path(1, "/".to_string()); diff --git a/cli/src/main.rs b/cli/src/main.rs index 91e710e9..21204f85 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -4,7 +4,7 @@ use clap_complete::CompleteEnv; use agentfs::{ cmd::{self, completions::handle_completions}, get_runtime, - parser::{Args, Command, FsCommand}, + parser::{Args, Command, FsCommand, SyncCommand}, }; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -32,17 +32,52 @@ fn main() { match args.command { Command::Init { id, - sync_config_path, force, base, + sync, } => { let rt = get_runtime(); - if let Err(e) = rt.block_on(cmd::init::init_database(id, sync_config_path, force, base)) - { + if let Err(e) = rt.block_on(cmd::init::init_database(id, sync, force, base)) { eprintln!("Error: {}", e); std::process::exit(1); } } + Command::Sync { + id_or_path, + command, + } => match command { + SyncCommand::Pull => { + let rt = get_runtime(); + if let Err(e) = rt.block_on(cmd::sync::handle_pull_command(id_or_path)) { + eprintln!("Error: {}", e); + std::process::exit(1); + } + } + SyncCommand::Push => { + let rt = get_runtime(); + if let Err(e) = rt.block_on(cmd::sync::handle_push_command(id_or_path)) { + eprintln!("Error: {}", e); + std::process::exit(1); + } + } + SyncCommand::Checkpoint => { + let rt = get_runtime(); + if let Err(e) = rt.block_on(cmd::sync::handle_checkpoint_command(id_or_path)) { + eprintln!("Error: {}", e); + std::process::exit(1); + } + } + SyncCommand::Stats => { + let rt = get_runtime(); + if let Err(e) = rt.block_on(cmd::sync::handle_stats_command( + &mut std::io::stdout(), + id_or_path, + )) { + eprintln!("Error: {}", e); + std::process::exit(1); + } + } + }, Command::Run { allow, no_default_allows, @@ -66,7 +101,6 @@ fn main() { } Command::Mount { id_or_path, - sync_config_path, mountpoint, auto_unmount, allow_root, @@ -76,7 +110,6 @@ fn main() { } => { if let Err(e) = cmd::mount(cmd::MountArgs { id_or_path, - sync_config_path, mountpoint, auto_unmount, allow_root, @@ -88,12 +121,9 @@ fn main() { std::process::exit(1); } } - Command::Diff { - id_or_path, - sync_config_path, - } => { + Command::Diff { id_or_path } => { let rt = get_runtime(); - if let Err(e) = rt.block_on(cmd::fs::diff_filesystem(id_or_path, sync_config_path)) { + if let Err(e) = rt.block_on(cmd::fs::diff_filesystem(id_or_path)) { eprintln!("Error: {}", e); std::process::exit(1); } @@ -101,7 +131,6 @@ fn main() { Command::Fs { command, id_or_path, - sync_config_path, } => { let rt = get_runtime(); match command { @@ -109,7 +138,6 @@ fn main() { if let Err(e) = rt.block_on(cmd::fs::ls_filesystem( &mut std::io::stdout(), id_or_path, - sync_config_path, &fs_path, )) { eprintln!("Error: {}", e); @@ -120,7 +148,6 @@ fn main() { if let Err(e) = rt.block_on(cmd::fs::cat_filesystem( &mut std::io::stdout(), id_or_path, - sync_config_path, &file_path, )) { eprintln!("Error: {}", e); @@ -133,17 +160,11 @@ fn main() { #[cfg(unix)] Command::Nfs { id_or_path, - sync_config_path, bind, port, } => { let rt = get_runtime(); - if let Err(e) = rt.block_on(cmd::nfs::handle_nfs_command( - id_or_path, - sync_config_path, - bind, - port, - )) { + if let Err(e) = rt.block_on(cmd::nfs::handle_nfs_command(id_or_path, bind, port)) { eprintln!("Error: {}", e); std::process::exit(1); } diff --git a/cli/src/parser.rs b/cli/src/parser.rs index cafc229d..751536d2 100644 --- a/cli/src/parser.rs +++ b/cli/src/parser.rs @@ -4,9 +4,7 @@ use clap::{Parser, Subcommand}; use clap_complete::{ engine::ValueCompleter, ArgValueCompleter, CompletionCandidate, PathCompleter, }; -use serde::Deserialize; use std::path::{Path, PathBuf}; -use turso::sync::PartialSyncOpts; #[derive(Parser, Debug)] #[command(name = "agentfs")] @@ -17,21 +15,18 @@ pub struct Args { pub command: Command, } -#[derive(Debug, Deserialize)] -pub struct SyncConfig { - pub remote_url: String, - pub auth_token: Option, - pub partial_sync_experimental: Option, -} - -impl SyncConfig { - pub fn parse(path: Option) -> anyhow::Result> { - let Some(path) = path else { - return Ok(None); - }; - let data = std::fs::read_to_string(path)?; - Ok(Some(serde_json::from_str(&data)?)) - } +#[derive(Debug, Parser)] +pub struct SyncCommandOptions { + #[arg(long)] + pub sync_remote_url: Option, + #[arg(long)] + pub sync_partial_prefetch: Option, + #[arg(long)] + pub sync_partial_segment_size: Option, + #[arg(long)] + pub sync_partial_bootstrap_query: Option, + #[arg(long)] + pub sync_partial_bootstrap_length: Option, } #[derive(Subcommand, Debug)] @@ -54,8 +49,17 @@ pub enum Command { #[arg(long)] base: Option, - #[arg(long)] - sync_config_path: Option, + #[command(flatten)] + sync: SyncCommandOptions, + }, + /// Remote sync operations + Sync { + /// Agent ID or database path + #[arg(add = ArgValueCompleter::new(id_or_path_completer))] + id_or_path: String, + + #[command(subcommand)] + command: SyncCommand, }, /// Filesystem operations Fs { @@ -63,9 +67,6 @@ pub enum Command { #[arg(add = ArgValueCompleter::new(id_or_path_completer))] id_or_path: String, - #[arg(long)] - sync_config_path: Option, - #[command(subcommand)] command: FsCommand, }, @@ -105,9 +106,6 @@ pub enum Command { #[arg(value_name = "ID_OR_PATH", add = ArgValueCompleter::new(id_or_path_completer))] id_or_path: String, - #[arg(long)] - sync_config_path: Option, - /// Mount point directory #[arg(value_name = "MOUNTPOINT", add = ArgValueCompleter::new(PathCompleter::dir()))] mountpoint: PathBuf, @@ -137,9 +135,6 @@ pub enum Command { /// Agent ID or database path #[arg(value_name = "ID_OR_PATH", add = ArgValueCompleter::new(id_or_path_completer))] id_or_path: String, - - #[arg(long)] - sync_config_path: Option, }, /// Start an NFS server to export an AgentFS filesystem over the network #[cfg(unix)] @@ -148,9 +143,6 @@ pub enum Command { #[arg(value_name = "ID_OR_PATH", add = ArgValueCompleter::new(id_or_path_completer))] id_or_path: String, - #[arg(long)] - sync_config_path: Option, - /// IP address to bind to #[arg(long, default_value = "127.0.0.1")] bind: String, @@ -176,6 +168,18 @@ pub enum FsCommand { }, } +#[derive(Subcommand, Debug)] +pub enum SyncCommand { + /// Pull remote changes (only of agentfs was initialized with remote sync) + Pull, + /// Push remote changes (only of agentfs was initialized with remote sync) + Push, + /// Print synced database stats + Stats, + /// Checkpoint local synced db + Checkpoint, +} + #[derive(Subcommand, Debug, Clone, Copy)] pub enum CompletionsCommand { /// Install shell completions to your shell rc file diff --git a/cli/src/sandbox/overlay.rs b/cli/src/sandbox/overlay.rs index 7cdb6e34..997b33e0 100644 --- a/cli/src/sandbox/overlay.rs +++ b/cli/src/sandbox/overlay.rs @@ -117,7 +117,7 @@ pub async fn run_cmd( // Start FUSE in a separate thread let fuse_handle = std::thread::spawn(move || { let rt = crate::get_runtime(); - crate::fuse::mount(overlay, None, fuse_opts, rt) + crate::fuse::mount(overlay, fuse_opts, rt) }); // Wait for FUSE mount to be ready diff --git a/sandbox/Cargo.lock b/sandbox/Cargo.lock index 81e79477..cf235812 100644 --- a/sandbox/Cargo.lock +++ b/sandbox/Cargo.lock @@ -2652,7 +2652,7 @@ dependencies = [ [[package]] name = "turso" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "mimalloc", "thiserror 2.0.17", @@ -2665,7 +2665,7 @@ dependencies = [ [[package]] name = "turso_core" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "aegis", "aes", @@ -2717,7 +2717,7 @@ dependencies = [ [[package]] name = "turso_ext" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "chrono", "getrandom 0.3.4", @@ -2727,7 +2727,7 @@ dependencies = [ [[package]] name = "turso_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "proc-macro2", "quote", @@ -2737,9 +2737,10 @@ dependencies = [ [[package]] name = "turso_parser" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "bitflags 2.10.0", + "memchr", "miette", "strum", "strum_macros", @@ -2750,7 +2751,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "bindgen", "env_logger", @@ -2764,7 +2765,7 @@ dependencies = [ [[package]] name = "turso_sdk_kit_macros" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "proc-macro2", "quote", @@ -2774,7 +2775,7 @@ dependencies = [ [[package]] name = "turso_sync_engine" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "base64", "bytes", @@ -2795,7 +2796,7 @@ dependencies = [ [[package]] name = "turso_sync_sdk_kit" version = "0.4.0-pre.18" -source = "git+https://github.com/tursodatabase/turso?rev=bd2b3314a0d860ac8bde2fe560650f7d753591eb#bd2b3314a0d860ac8bde2fe560650f7d753591eb" +source = "git+https://github.com/tursodatabase/turso?rev=00b0890c8fef7f1ca58ed24da2c1a872816e2c1a#00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" dependencies = [ "bindgen", "env_logger", diff --git a/sdk/rust/Cargo.toml b/sdk/rust/Cargo.toml index 90eb7687..a509259a 100644 --- a/sdk/rust/Cargo.toml +++ b/sdk/rust/Cargo.toml @@ -6,7 +6,7 @@ description = "AgentFS SDK for Rust" license = "MIT" [dependencies] -turso = { git = "https://github.com/tursodatabase/turso", rev = "bd2b3314a0d860ac8bde2fe560650f7d753591eb" } +turso = { git = "https://github.com/tursodatabase/turso", rev = "00b0890c8fef7f1ca58ed24da2c1a872816e2c1a" } tokio = { version = "1", features = ["full"] } async-trait = "0.1" serde = { version = "1.0", features = ["derive"] } From 5005860fd2e1e175bca10538322f7803ea083ce1 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Fri, 26 Dec 2025 22:02:53 +0400 Subject: [PATCH 11/11] small fixes --- cli/src/cmd/fs.rs | 2 +- cli/src/cmd/init.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/cmd/fs.rs b/cli/src/cmd/fs.rs index c0e8eb91..50585b5e 100644 --- a/cli/src/cmd/fs.rs +++ b/cli/src/cmd/fs.rs @@ -1,4 +1,4 @@ -use std::{collections::VecDeque, path::PathBuf}; +use std::collections::VecDeque; use agentfs_sdk::AgentFSOptions; use anyhow::{Context, Result as AnyhowResult}; diff --git a/cli/src/cmd/init.rs b/cli/src/cmd/init.rs index 06216944..670fa026 100644 --- a/cli/src/cmd/init.rs +++ b/cli/src/cmd/init.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; -use agentfs_sdk::{agentfs_dir, AgentFS, AgentFSOptions}; +use agentfs_sdk::{agentfs_dir, AgentFS, AgentFSOptions, OverlayFS}; use anyhow::{Context, Result as AnyhowResult}; use turso::sync::{PartialBootstrapStrategy, PartialSyncOpts};