diff --git a/clients/agent-runtime/Cargo.lock b/clients/agent-runtime/Cargo.lock index 3ce1bfc43..f4419427c 100755 --- a/clients/agent-runtime/Cargo.lock +++ b/clients/agent-runtime/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "addr" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93b8a41dbe230ad5087cc721f8d41611de654542180586b315d9f4cf6b72bef" +dependencies = [ + "psl-types", +] + [[package]] name = "adler2" version = "2.0.1" @@ -24,7 +39,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -38,6 +53,17 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -45,6 +71,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -65,6 +92,19 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "ammonia" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17e913097e1a2124b46746c980134e8c954bc17a6a59bb3fde96f088d126dde6" +dependencies = [ + "cssparser", + "html5ever", + "maplit", + "tendril", + "url", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -130,12 +170,36 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "any_ascii" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90c6333e01ba7235575b6ab53e5af10f1c327927fd97c36462917e289557ea64" + [[package]] name = "anyhow" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +[[package]] +name = "approx" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" +dependencies = [ + "num-traits", +] + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "ar_archive_writer" version = "0.5.1" @@ -145,6 +209,148 @@ dependencies = [ "object 0.37.3", ] +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-slice" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" +dependencies = [ + "generic-array 0.12.4", + "generic-array 0.13.3", + "generic-array 0.14.7", + "stable_deref_trait", +] + +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-graphql" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1057a9f7ccf2404d94571dec3451ade1cb524790df6f1ada0d19c2a49f6b0f40" +dependencies = [ + "async-graphql-derive", + "async-graphql-parser", + "async-graphql-value", + "async-io", + "async-trait", + "asynk-strim", + "base64 0.22.1", + "bytes", + "fnv", + "futures-util", + "http 1.4.0", + "indexmap 2.13.0", + "mime", + "multer", + "num-traits", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "static_assertions_next", + "thiserror 2.0.18", +] + +[[package]] +name = "async-graphql-derive" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e6cbeadc8515e66450fba0985ce722192e28443697799988265d86304d7cc68" +dependencies = [ + "Inflector", + "async-graphql-parser", + "darling 0.23.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "strum", + "syn 2.0.116", + "thiserror 2.0.18", +] + +[[package]] +name = "async-graphql-parser" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64ef70f77a1c689111e52076da1cd18f91834bcb847de0a9171f83624b07fbf" +dependencies = [ + "async-graphql-value", + "pest", + "serde", + "serde_json", +] + +[[package]] +name = "async-graphql-value" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e3ef112905abea9dea592fc868a6873b10ebd3f983e83308f995d6284e9ba41" +dependencies = [ + "bytes", + "indexmap 2.13.0", + "serde", + "serde_json", +] + [[package]] name = "async-io" version = "2.6.0" @@ -163,6 +369,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.89" @@ -171,7 +383,37 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version", +] + +[[package]] +name = "asynk-strim" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52697735bdaac441a29391a9e97102c74c6ef0f9b60a40cf109b1b404e29d2f6" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", ] [[package]] @@ -215,7 +457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" dependencies = [ "axum-core", - "base64", + "base64 0.22.1", "bytes", "form_urlencoded", "futures-util", @@ -261,12 +503,46 @@ dependencies = [ "tower-service", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bcrypt" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e65938ed058ef47d92cf8b346cc76ef48984572ade631927e9937b5ffc7662c7" +dependencies = [ + "base64 0.22.1", + "blowfish", + "getrandom 0.2.17", + "subtle", + "zeroize", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bincode" version = "2.0.1" @@ -287,6 +563,21 @@ dependencies = [ "virtue", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitfield" version = "0.19.4" @@ -304,7 +595,7 @@ checksum = "f48d6ace212fdf1b45fd6b566bb40808415344642b76c3224c07c8df9da81e97" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -334,13 +625,36 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "blake3" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures", +] + [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -349,7 +663,40 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ - "generic-array", + "generic-array 0.14.7", +] + +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.116", ] [[package]] @@ -358,6 +705,28 @@ version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytecount" version = "0.6.9" @@ -381,7 +750,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -395,6 +764,9 @@ name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "serde", +] [[package]] name = "cast" @@ -402,6 +774,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + [[package]] name = "cbc" version = "0.1.2" @@ -423,6 +804,63 @@ dependencies = [ "shlex", ] +[[package]] +name = "cedar-policy" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d91e3b10a0f7f2911774d5e49713c4d25753466f9e11d1cd2ec627f8a2dc857" +dependencies = [ + "cedar-policy-core", + "cedar-policy-validator", + "itertools 0.10.5", + "lalrpop-util", + "ref-cast", + "serde", + "serde_json", + "smol_str", + "thiserror 1.0.69", +] + +[[package]] +name = "cedar-policy-core" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd2315591c6b7e18f8038f0a0529f254235fd902b6c217aabc04f2459b0d9995" +dependencies = [ + "either", + "ipnet", + "itertools 0.10.5", + "lalrpop", + "lalrpop-util", + "lazy_static", + "miette 5.10.0", + "regex", + "rustc_lexer", + "serde", + "serde_json", + "serde_with", + "smol_str", + "stacker", + "thiserror 1.0.69", +] + +[[package]] +name = "cedar-policy-validator" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e756e1b2a5da742ed97e65199ad6d0893e9aa4bd6b34be1de9e70bd1e6adc7df" +dependencies = [ + "cedar-policy-core", + "itertools 0.10.5", + "serde", + "serde_json", + "serde_with", + "smol_str", + "stacker", + "thiserror 1.0.69", + "unicode-security", +] + [[package]] name = "cff-parser" version = "0.1.0" @@ -486,7 +924,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" dependencies = [ "chrono", - "phf", + "phf 0.12.1", ] [[package]] @@ -568,7 +1006,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -592,7 +1030,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ef0193218d365c251b5b9297f9911a908a8ddd2ebd3a36cc5d0ef0f63aee9e" dependencies = [ - "heapless", + "heapless 0.9.2", "thiserror 2.0.18", ] @@ -624,6 +1062,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + [[package]] name = "cookie" version = "0.16.2" @@ -687,7 +1131,7 @@ dependencies = [ "anyhow", "async-trait", "axum", - "base64", + "base64 0.22.1", "chacha20poly1305", "chrono", "chrono-tz", @@ -729,6 +1173,7 @@ dependencies = [ "serde_json", "sha2", "shellexpand", + "surrealdb", "tempfile", "thiserror 2.0.18", "tokio", @@ -752,7 +1197,7 @@ version = "0.1.2" dependencies = [ "anyhow", "async-trait", - "base64", + "base64 0.22.1", "chrono", "directories 5.0.1", "reqwest", @@ -823,6 +1268,12 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "cron" version = "0.15.0" @@ -871,11 +1322,34 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", "typenum", ] +[[package]] +name = "cssparser" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e901edd733a1472f944a45116df3f846f54d37e67e68640ac8bb69689aca2aa" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.11.3", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.116", +] + [[package]] name = "csv" version = "1.4.0" @@ -903,8 +1377,28 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] @@ -918,7 +1412,34 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.116", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.116", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.116", ] [[package]] @@ -927,9 +1448,44 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", "quote", - "syn", + "syn 2.0.116", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] @@ -956,11 +1512,11 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58cb0719583cbe4e81fb40434ace2f0d22ccc3e39a74bb3796c22b451b4f139d" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -970,8 +1526,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4" dependencies = [ "powerfmt", + "serde_core", ] +[[package]] +name = "deunicode" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" + [[package]] name = "dialoguer" version = "0.12.0" @@ -1023,6 +1586,16 @@ dependencies = [ "dirs-sys 0.5.0", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + [[package]] name = "dirs-sys" version = "0.4.1" @@ -1044,7 +1617,18 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users 0.4.6", + "winapi", ] [[package]] @@ -1055,7 +1639,17 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", +] + +[[package]] +name = "dmp" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2dfc7a18dffd3ef60a442b72a827126f1557d914620f8fc4d1049916da43c1" +dependencies = [ + "trice", + "urlencoding", ] [[package]] @@ -1068,14 +1662,29 @@ dependencies = [ ] [[package]] -name = "docsplay-macros" -version = "0.1.2" +name = "docsplay-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11772ed3eb3db124d826f3abeadf5a791a557f62c19b123e3f07288158a71fdd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11772ed3eb3db124d826f3abeadf5a791a557f62c19b123e3f07288158a71fdd" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" dependencies = [ - "proc-macro2", - "quote", - "syn", + "dtoa", ] [[package]] @@ -1084,6 +1693,22 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "earcutr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01" +dependencies = [ + "itertools 0.11.0", + "num-traits", +] + [[package]] name = "ecb" version = "0.1.2" @@ -1105,7 +1730,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9298e6504d9b9e780ed3f7dfd43a61be8cd0e09eb07f7706a945b0072b6670b6" dependencies = [ - "base64", + "base64 0.22.1", "memchr", ] @@ -1115,6 +1740,15 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +[[package]] +name = "ena" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" +dependencies = [ + "log", +] + [[package]] name = "encode_unicode" version = "1.0.0" @@ -1130,6 +1764,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "enumflags2" version = "0.7.12" @@ -1147,7 +1787,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -1163,7 +1803,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1190,7 +1830,7 @@ version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46f05d15cb2479a3cbbbe684b9f0831b2ae036d9faefd1eb08f21267275862f9" dependencies = [ - "base64", + "base64 0.22.1", "bitflags 2.11.0", "bytemuck", "esp-idf-part", @@ -1199,7 +1839,7 @@ dependencies = [ "libc", "log", "md-5", - "miette", + "miette 7.6.0", "nix 0.30.1", "object 0.38.1", "serde", @@ -1217,6 +1857,27 @@ dependencies = [ "num-traits", ] +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -1235,7 +1896,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d0086bcd59795408c87a04f94b5a8bd62cba2856cfe656c7e6439061d95b760" dependencies = [ - "base64", + "base64 0.22.1", "cookie 0.18.1", "futures-util", "http 1.4.0", @@ -1264,6 +1925,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.1.9" @@ -1274,6 +1941,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + [[package]] name = "fnv" version = "1.0.7" @@ -1307,12 +1980,28 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fst" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" + [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" version = "0.3.32" @@ -1367,7 +2056,10 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ + "fastrand", "futures-core", + "futures-io", + "parking", "pin-project-lite", ] @@ -1379,7 +2071,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -1420,6 +2112,24 @@ dependencies = [ "thread_local", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1430,6 +2140,49 @@ dependencies = [ "version_check", ] +[[package]] +name = "geo" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f811f663912a69249fa620dcd2a005db7254529da2d8a0b23942e81f47084501" +dependencies = [ + "earcutr", + "float_next_after", + "geo-types", + "geographiclib-rs", + "log", + "num-traits", + "robust", + "rstar 0.12.2", + "serde", + "spade", +] + +[[package]] +name = "geo-types" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f8647af4005fa11da47cd56252c6ef030be8fa97bdbf355e7dfb6348f0a82c" +dependencies = [ + "approx 0.5.1", + "num-traits", + "rstar 0.10.0", + "rstar 0.11.0", + "rstar 0.12.2", + "rstar 0.8.4", + "rstar 0.9.3", + "serde", +] + +[[package]] +name = "geographiclib-rs" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5a7f08910fd98737a6eda7568e7c5e645093e073328eeef49758cfe8b0489c7" +dependencies = [ + "libm", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -1477,7 +2230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 2.13.0", "stable_deref_trait", ] @@ -1498,6 +2251,24 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hash32" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hash32" version = "0.3.1" @@ -1507,13 +2278,22 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash", + "ahash 0.8.12", "allocator-api2", ] @@ -1523,6 +2303,8 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.1.5", ] @@ -1543,7 +2325,7 @@ checksum = "149e3ea90eb5a26ad354cfe3cb7f7401b9329032d0235f2687d03a35f30e5d4c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -1555,13 +2337,48 @@ dependencies = [ "hashbrown 0.16.1", ] +[[package]] +name = "heapless" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" +dependencies = [ + "as-slice", + "generic-array 0.14.7", + "hash32 0.1.1", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32 0.2.1", + "rustc_version", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", + "stable_deref_trait", +] + [[package]] name = "heapless" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" dependencies = [ - "hash32", + "hash32 0.3.1", "stable_deref_trait", ] @@ -1624,6 +2441,17 @@ dependencies = [ "windows-link", ] +[[package]] +name = "html5ever" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55d958c2f74b664487a2035fe1dadb032c48718a03b63f3ab0b8537db8549ed4" +dependencies = [ + "log", + "markup5ever", + "match_token", +] + [[package]] name = "http" version = "0.2.12" @@ -1680,6 +2508,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + [[package]] name = "hyper" version = "1.8.1" @@ -1727,7 +2561,7 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-util", @@ -1756,7 +2590,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.2", ] [[package]] @@ -1898,7 +2732,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -1962,6 +2796,17 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "365a784774bb381e8c19edb91190a90d7f2625e057b55de2bc0f6b57bc779ff2" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -1981,7 +2826,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "block-padding", - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -2029,7 +2874,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2047,6 +2892,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -2091,6 +2945,52 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "lalrpop" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" +dependencies = [ + "ascii-canvas", + "bit-set", + "ena", + "itertools 0.11.0", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata", +] + [[package]] name = "landlock" version = "0.4.4" @@ -2120,7 +3020,7 @@ version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e13e10e8818f8b2a60f52cb127041d388b89f3a96a62be9ceaffa22262fef7f" dependencies = [ - "base64", + "base64 0.22.1", "chumsky", "email-encoding", "email_address", @@ -2138,6 +3038,15 @@ dependencies = [ "webpki-roots 1.0.6", ] +[[package]] +name = "lexicmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7378d131ddf24063b32cbd7e91668d183140c4b3906270635a4d633d1068ea5d" +dependencies = [ + "any_ascii", +] + [[package]] name = "libc" version = "0.2.182" @@ -2181,6 +3090,18 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "linfa-linalg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e7562b41c8876d3367897067013bb2884cc78e6893f092ecd26b305176ac82" +dependencies = [ + "ndarray", + "num-traits", + "rand 0.8.5", + "thiserror 1.0.69", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -2239,7 +3160,7 @@ dependencies = [ "encoding_rs", "flate2", "getrandom 0.3.4", - "indexmap", + "indexmap 2.13.0", "itoa", "log", "md-5", @@ -2260,6 +3181,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + [[package]] name = "mach2" version = "0.4.3" @@ -2278,6 +3205,34 @@ dependencies = [ "hashify", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "markup5ever" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "311fe69c934650f8f19652b3946075f0fc41ad8757dbb68f1ca14e7900ecc1c3" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "match_token" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "matchers" version = "0.2.0" @@ -2293,6 +3248,16 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "md-5" version = "0.10.6" @@ -2309,6 +3274,18 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "miette" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" +dependencies = [ + "miette-derive 5.10.0", + "once_cell", + "thiserror 1.0.69", + "unicode-width 0.1.14", +] + [[package]] name = "miette" version = "7.6.0" @@ -2316,10 +3293,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ "cfg-if", - "miette-derive", + "miette-derive 7.6.0", "unicode-width 0.1.14", ] +[[package]] +name = "miette-derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "miette-derive" version = "7.6.0" @@ -2328,7 +3316,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -2382,6 +3370,67 @@ dependencies = [ "winapi", ] +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.4.0", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + +[[package]] +name = "ndarray" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32" +dependencies = [ + "approx 0.4.0", + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "rawpointer", +] + +[[package]] +name = "ndarray-stats" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af5a8477ac96877b5bd1fd67e0c28736c12943aba24eda92b127e036b0c8f400" +dependencies = [ + "indexmap 1.9.3", + "itertools 0.10.5", + "ndarray", + "noisy_float", + "num-integer", + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + [[package]] name = "nix" version = "0.26.4" @@ -2437,6 +3486,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "noisy_float" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c16843be85dd410c6a12251c4eca0dd1d3ee8c5725f746c4d5e0fdcec0a864b2" +dependencies = [ + "num-traits", +] + [[package]] name = "nom" version = "8.0.0" @@ -2457,13 +3515,41 @@ dependencies = [ "nom", ] +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", ] [[package]] @@ -2472,6 +3558,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2479,6 +3574,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi 0.5.2", + "libc", ] [[package]] @@ -2538,6 +3644,30 @@ dependencies = [ "ruzstd", ] +[[package]] +name = "object_store" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbfbfff40aeccab00ec8a910b57ca8ecf4319b335c542f2edcd19dd25a1e2a00" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "futures", + "http 1.4.0", + "humantime", + "itertools 0.14.0", + "parking_lot", + "percent-encoding", + "thiserror 2.0.18", + "tokio", + "tracing", + "url", + "walkdir", + "wasm-bindgen-futures", + "web-time", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -2682,6 +3812,35 @@ dependencies = [ "num-traits", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "path-clean" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + [[package]] name = "pdf-extract" version = "0.10.0" @@ -2699,19 +3858,119 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "pdqselect" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64 0.22.1", + "serde_core", +] + [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.13.0", +] + +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared 0.11.3", +] + [[package]] name = "phf" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" dependencies = [ - "phf_shared", + "phf_shared 0.12.1", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn 2.0.116", + "unicase", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", + "unicase", ] [[package]] @@ -2723,6 +3982,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.10" @@ -2740,7 +4005,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -2850,6 +4115,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "prettyplease" version = "0.2.37" @@ -2857,7 +4128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.116", ] [[package]] @@ -2868,7 +4139,7 @@ checksum = "7ee27329ac37fa02b194c62a4e3c1aa053739884ea7bcf861249866d3bf7de00" dependencies = [ "anyhow", "async-io", - "bincode", + "bincode 2.0.1", "bitfield", "bitvec", "cobs", @@ -2902,8 +4173,8 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2239aca5dc62c68ca6d8ff0051fe617cb8363b803380fbc60567e67c82b474df" dependencies = [ - "base64", - "indexmap", + "base64 0.22.1", + "indexmap 2.13.0", "jep106", "serde", "serde_with", @@ -2962,9 +4233,15 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + [[package]] name = "psm" version = "0.1.30" @@ -2975,6 +4252,38 @@ dependencies = [ "cc", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quick_cache" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb55a1aa7668676bb93926cd4e9cdfe60f03bb866553bcca9112554911b6d3dc" +dependencies = [ + "ahash 0.8.12", + "equivalent", + "hashbrown 0.14.5", + "parking_lot", +] + [[package]] name = "quinn" version = "0.11.9" @@ -3027,7 +4336,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3057,6 +4366,17 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", + "serde", +] + [[package]] name = "rand" version = "0.8.5" @@ -3122,6 +4442,12 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rayon" version = "1.11.0" @@ -3142,6 +4468,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "reblessive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc4a4ea2a66a41a1152c4b3d86e8954dc087bdf33af35446e6e176db4e73c8c" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -3173,6 +4505,26 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "regex" version = "1.12.3" @@ -3200,7 +4552,16 @@ dependencies = [ name = "regex-syntax" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] [[package]] name = "reqwest" @@ -3208,7 +4569,7 @@ version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-core", @@ -3245,6 +4606,32 @@ dependencies = [ "webpki-roots 1.0.6", ] +[[package]] +name = "revision" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b8ee532f15b2f0811eb1a50adf10d036e14a6cdae8d99893e7f3b921cb227d" +dependencies = [ + "chrono", + "geo", + "regex", + "revision-derive", + "roaring", + "rust_decimal", + "uuid", +] + +[[package]] +name = "revision-derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3415e1bc838c36f9a0a2ac60c0fa0851c72297685e66592c44870d82834dfa2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "ring" version = "0.17.14" @@ -3259,6 +4646,35 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rmp" version = "0.8.15" @@ -3278,6 +4694,32 @@ dependencies = [ "serde", ] +[[package]] +name = "rmpv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a4e1d4b9b938a26d2996af33229f0ca0956c652c1375067f0b45291c1df8417" +dependencies = [ + "rmp", +] + +[[package]] +name = "roaring" +version = "0.10.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e8d2cfa184d94d0726d650a9f4a1be7f9b76ac9fdb954219878dc00c1c1e7b" +dependencies = [ + "bytemuck", + "byteorder", + "serde", +] + +[[package]] +name = "robust" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e27ee8bb91ca0adcf0ecb116293afa12d393f9c2b9b9cd54d33e8078fe19839" + [[package]] name = "rppal" version = "0.19.0" @@ -3306,6 +4748,67 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "rstar" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a45c0e8804d37e4d97e55c6f258bc9ad9c5ee7b07437009dd152d764949a27c" +dependencies = [ + "heapless 0.6.1", + "num-traits", + "pdqselect", + "serde", + "smallvec", +] + +[[package]] +name = "rstar" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b40f1bfe5acdab44bc63e6699c28b74f75ec43afb59f3eda01e145aff86a25fa" +dependencies = [ + "heapless 0.7.17", + "num-traits", + "serde", + "smallvec", +] + +[[package]] +name = "rstar" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f39465655a1e3d8ae79c6d9e007f4953bfc5d55297602df9dc38f9ae9f1359a" +dependencies = [ + "heapless 0.7.17", + "num-traits", + "serde", + "smallvec", +] + +[[package]] +name = "rstar" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73111312eb7a2287d229f06c00ff35b51ddee180f017ab6dec1f69d62ac098d6" +dependencies = [ + "heapless 0.7.17", + "num-traits", + "serde", + "smallvec", +] + +[[package]] +name = "rstar" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421400d13ccfd26dfa5858199c30a5d76f9c54e0dba7575273025b43c5175dbb" +dependencies = [ + "heapless 0.8.0", + "num-traits", + "serde", + "smallvec", +] + [[package]] name = "rusqlite" version = "0.38.0" @@ -3321,12 +4824,56 @@ dependencies = [ "sqlite-wasm-rs", ] +[[package]] +name = "rust-stemmers" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "rust_decimal" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f703d19852dbf87cbc513643fa81428361eb6940f1ac14fd58155d295a3eb0" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_lexer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86aae0c77166108c01305ee1a36a1e77289d7dc6ca0a3cd91ff4992de2d16a5" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" @@ -3350,7 +4897,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3424,6 +4971,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -3442,6 +4998,30 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -3454,6 +5034,24 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add" +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "password-hash", + "pbkdf2", + "salsa20", + "sha2", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "security-framework" version = "3.6.0" @@ -3482,6 +5080,16 @@ name = "semver" version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" @@ -3493,6 +5101,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-content" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3753ca04f350fa92d00b6146a3555e63c55388c9ef2e11e09bce2ff1c0b509c6" +dependencies = [ + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -3510,7 +5127,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -3519,6 +5136,7 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ + "indexmap 2.13.0", "itoa", "memchr", "serde", @@ -3582,22 +5200,38 @@ version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ - "base64", + "base64 0.22.1", "chrono", "hex", - "indexmap", + "indexmap 1.9.3", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.1", "serde_core", "serde_json", + "serde_with_macros", "time", ] +[[package]] +name = "serde_with_macros" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap", + "indexmap 2.13.0", "itoa", "ryu", "serde", @@ -3690,6 +5324,24 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "simple_asn1" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.18", + "time", +] + [[package]] name = "siphasher" version = "1.0.2" @@ -3708,6 +5360,21 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + [[package]] name = "socket2" version = "0.6.2" @@ -3718,6 +5385,27 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spade" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb313e1c8afee5b5647e00ee0fe6855e3d529eb863a0fdae1d60006c4d1e9990" +dependencies = [ + "hashbrown 0.15.5", + "num-traits", + "robust", + "smallvec", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "sqlite-wasm-rs" version = "0.5.2" @@ -3734,19 +5422,62 @@ dependencies = [ name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stacker" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "static_assertions_next" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7beae5182595e9a8b683fa98c4317f956c9a2dec3b9716990d20023cc60c766" + +[[package]] +name = "storekey" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c42833834a5d23b344f71d87114e0cc9994766a5c42938f4b50e7b2aef85b2" +dependencies = [ + "byteorder", + "memchr", + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.11.3", + "precomputed-hash", + "serde", +] [[package]] -name = "stacker" -version = "0.1.23" +name = "string_cache_codegen" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.59.0", + "phf_generator", + "phf_shared 0.11.3", + "proc-macro2", + "quote", ] [[package]] @@ -3784,7 +5515,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -3793,6 +5524,147 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "surrealdb" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9ea74b958ed0069fe920db48ae6271ea18eaa9ad4be60ac66509adc42dc6c6" +dependencies = [ + "arrayvec", + "async-channel", + "bincode 1.3.3", + "chrono", + "dmp", + "futures", + "geo", + "getrandom 0.3.4", + "indexmap 2.13.0", + "path-clean", + "pharos", + "reblessive", + "reqwest", + "revision", + "ring", + "rust_decimal", + "rustls", + "rustls-pki-types", + "semver", + "serde", + "serde-content", + "serde_json", + "surrealdb-core", + "thiserror 1.0.69", + "tokio", + "tokio-tungstenite 0.23.1", + "tokio-util", + "tracing", + "trice", + "url", + "uuid", + "wasm-bindgen-futures", + "wasmtimer", + "ws_stream_wasm", +] + +[[package]] +name = "surrealdb-core" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6e0caa001aac8cc6412335d2988f290d47ef4b47a8a0d942b419913c0e963c" +dependencies = [ + "addr", + "ahash 0.8.12", + "ammonia", + "any_ascii", + "argon2", + "async-channel", + "async-executor", + "async-graphql", + "base64 0.21.7", + "bcrypt", + "bincode 1.3.3", + "blake3", + "bytes", + "castaway", + "cedar-policy", + "chrono", + "ciborium", + "dashmap", + "deunicode", + "dmp", + "fst", + "futures", + "fuzzy-matcher", + "geo", + "geo-types", + "getrandom 0.3.4", + "hashbrown 0.14.5", + "hex", + "http 1.4.0", + "ipnet", + "jsonwebtoken", + "lexicmp", + "linfa-linalg", + "md-5", + "ndarray", + "ndarray-stats", + "num-traits", + "num_cpus", + "object_store", + "parking_lot", + "pbkdf2", + "pharos", + "phf 0.11.3", + "pin-project-lite", + "quick_cache", + "radix_trie", + "rand 0.8.5", + "rayon", + "reblessive", + "regex", + "revision", + "ring", + "rmpv", + "roaring", + "rust-stemmers", + "rust_decimal", + "scrypt", + "semver", + "serde", + "serde-content", + "serde_json", + "sha1", + "sha2", + "snap", + "storekey", + "strsim", + "subtle", + "sysinfo", + "thiserror 1.0.69", + "tokio", + "tracing", + "trice", + "ulid", + "unicase", + "url", + "uuid", + "vart", + "wasm-bindgen-futures", + "wasmtimer", + "ws_stream_wasm", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.116" @@ -3821,7 +5693,21 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", +] + +[[package]] +name = "sysinfo" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows", ] [[package]] @@ -3840,7 +5726,29 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.3", - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", ] [[package]] @@ -3869,7 +5777,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -3880,7 +5788,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -3923,6 +5831,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.7.6" @@ -3991,7 +5908,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -4040,6 +5957,22 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "tokio-tungstenite" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite 0.23.0", + "webpki-roots 0.26.11", +] + [[package]] name = "tokio-tungstenite" version = "0.24.0" @@ -4076,6 +6009,7 @@ checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -4099,7 +6033,7 @@ version = "1.0.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1dfefef6a142e93f346b64c160934eb13b5594b84ab378133ac6815cb2bd57f" dependencies = [ - "indexmap", + "indexmap 2.13.0", "serde_core", "serde_spanned 1.0.4", "toml_datetime 1.0.0+spec-1.1.0", @@ -4141,7 +6075,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap", + "indexmap 2.13.0", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -4155,7 +6089,7 @@ version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap", + "indexmap 2.13.0", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "winnow 0.7.14", @@ -4189,7 +6123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f32a6f80051a4111560201420c7885d0082ba9efe2ab61875c587bb6b18b9a0" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "bytes", "http 1.4.0", "http-body", @@ -4280,7 +6214,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -4308,6 +6242,17 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "trice" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3aaab10ae9fac0b10f392752bf56f0fd20845f39037fec931e8537b105b515a" +dependencies = [ + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -4320,6 +6265,27 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.4.0", + "httparse", + "log", + "rand 0.8.5", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 1.0.69", + "url", + "utf-8", +] + [[package]] name = "tungstenite" version = "0.24.0" @@ -4378,6 +6344,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "udev" version = "0.8.0" @@ -4396,6 +6368,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca77d41ab27e3fa45df42043f96c79b80c6d8632eed906b54681d8d47ab00623" +[[package]] +name = "ulid" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" +dependencies = [ + "rand 0.9.2", + "serde", + "web-time", +] + [[package]] name = "unescaper" version = "0.1.8" @@ -4438,6 +6421,22 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +[[package]] +name = "unicode-script" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" + +[[package]] +name = "unicode-security" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e4ddba1535dd35ed8b61c52166b7155d7f4e4b8847cec6f48e71dc66d8b5e50" +dependencies = [ + "unicode-normalization", + "unicode-script", +] + [[package]] name = "unicode-width" version = "0.1.14" @@ -4529,9 +6528,16 @@ checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ "getrandom 0.4.1", "js-sys", + "serde_core", "wasm-bindgen", ] +[[package]] +name = "vart" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87782b74f898179396e93c0efabb38de0d58d50bbd47eae00c71b3a1144dbbae" + [[package]] name = "vcpkg" version = "0.2.15" @@ -4639,7 +6645,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.116", "wasm-bindgen-shared", ] @@ -4669,7 +6675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.13.0", "wasm-encoder", "wasmparser", ] @@ -4695,10 +6701,23 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.0", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.13.0", "semver", ] +[[package]] +name = "wasmtimer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7ed9d8b15c7fb594d72bfb4b5a276f3d2029333cd93a932f376f5937f6f80ee" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.85" @@ -4719,13 +6738,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" +dependencies = [ + "phf 0.11.3", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + [[package]] name = "webdriver" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d53921e1bef27512fa358179c9a22428d55778d2c2ae3c5c37a52b82ce6e92" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "cookie 0.16.2", "http 0.2.12", @@ -4785,7 +6816,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -4794,19 +6825,52 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.2", + "windows-interface 0.59.3", "windows-link", - "windows-result", + "windows-result 0.4.1", "windows-strings", ] +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "windows-implement" version = "0.60.2" @@ -4815,7 +6879,18 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", ] [[package]] @@ -4826,7 +6901,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -4835,6 +6910,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -5130,9 +7214,9 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.13.0", "prettyplease", - "syn", + "syn 2.0.116", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -5148,7 +7232,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.116", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -5161,7 +7245,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.0", - "indexmap", + "indexmap 2.13.0", "log", "serde", "serde_derive", @@ -5180,7 +7264,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.13.0", "log", "semver", "serde", @@ -5202,6 +7286,25 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "ws_stream_wasm" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version", + "send_wrapper", + "thiserror 2.0.18", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wyz" version = "0.5.1" @@ -5242,7 +7345,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", "synstructure", ] @@ -5254,7 +7357,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", "synstructure", ] @@ -5275,7 +7378,7 @@ checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -5295,7 +7398,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", "synstructure", ] @@ -5346,7 +7449,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] @@ -5357,7 +7460,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.116", ] [[package]] diff --git a/clients/agent-runtime/Cargo.toml b/clients/agent-runtime/Cargo.toml index a8434d763..c719ba3e3 100755 --- a/clients/agent-runtime/Cargo.toml +++ b/clients/agent-runtime/Cargo.toml @@ -82,6 +82,7 @@ prost = { version = "0.14", default-features = false } # Memory / persistence rusqlite = { version = "0.38", features = ["bundled"] } +surrealdb = { version = "2.3", optional = true, default-features = false, features = ["protocol-ws", "rustls"] } chrono = { version = "0.4", default-features = false, features = ["clock", "std", "serde"] } chrono-tz = "0.10" cron = "0.15" @@ -151,6 +152,8 @@ landlock = ["sandbox-landlock"] probe = ["dep:probe-rs"] # rag-pdf = PDF ingestion for datasheet RAG rag-pdf = ["dep:pdf-extract"] +# memory-surreal = SurrealDB-backed memory backend +memory-surreal = ["dep:surrealdb"] [profile.release] opt-level = "z" # Optimize for size lto = "thin" # Lower memory use during release builds diff --git a/clients/agent-runtime/README.md b/clients/agent-runtime/README.md index 021c2f884..7e0271c84 100755 --- a/clients/agent-runtime/README.md +++ b/clients/agent-runtime/README.md @@ -73,6 +73,9 @@ cd corvus cargo build --release cargo install --path . --force +# Enable SurrealDB memory backend support +cargo build --release --features memory-surreal + # Quick setup (no prompts) corvus onboard --api-key sk-... --provider openrouter @@ -131,7 +134,7 @@ Every subsystem is a **trait** — swap implementations with a config change, ze |-------------------|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------| | **AI Models** | `Provider` | 22+ providers (OpenRouter, Anthropic, OpenAI, Ollama, Venice, Groq, Mistral, xAI, DeepSeek, Together, Fireworks, Perplexity, Cohere, Bedrock, etc.) | `custom:https://your-api.com` — any OpenAI-compatible API | | **Channels** | `Channel` | CLI, Telegram, Discord, Slack, iMessage, Matrix, WhatsApp, Webhook | Any messaging API | -| **Memory** | `Memory` | SQLite with hybrid search (FTS5 + vector cosine similarity), Markdown | Any persistence backend | +| **Memory** | `Memory` | SQLite (hybrid search), SurrealDB (feature: `memory-surreal`), Markdown | Any persistence backend | | **Tools** | `Tool` | shell, file_read, file_write, memory_store, memory_recall, memory_forget, browser_open (Brave + allowlist), composio (optional) | Any capability | | **Observability** | `Observer` | Noop, Log, Multi | Prometheus, OTel | | **Runtime** | `RuntimeAdapter` | Native, Docker (sandboxed) | WASM (planned; unsupported kinds fail fast) | @@ -168,11 +171,17 @@ The agent automatically recalls, saves, and manages memory via tools. ```toml [memory] -backend = "sqlite" # "sqlite", "markdown", "none" +backend = "sqlite" # "sqlite", "lucid", "surreal", "markdown", "none" auto_save = true embedding_provider = "openai" vector_weight = 0.7 keyword_weight = 0.3 + +[memory.surreal] +url = "http://127.0.0.1:8000" +namespace = "corvus" +database = "memory" +allow_http_loopback = true ``` ## Security @@ -263,7 +272,8 @@ WhatsApp uses Meta's Cloud API with webhooks (push-based, not polling): ## Configuration -Config: `~/.corvus/config.toml` (created by `onboard`) +Config: `~/.corvus/config.toml` (created by `onboard`; wizard now includes SurrealDB prompts when +available) ```toml api_key = "sk-..." @@ -272,12 +282,21 @@ default_model = "anthropic/claude-sonnet-4-20250514" default_temperature = 0.7 [memory] -backend = "sqlite" # "sqlite", "markdown", "none" +backend = "sqlite" # "sqlite", "lucid", "surreal", "markdown", "none" auto_save = true embedding_provider = "openai" # "openai", "noop" vector_weight = 0.7 keyword_weight = 0.3 +[memory.surreal] +url = "http://127.0.0.1:8000" +namespace = "corvus" +database = "memory" +# username = "corvus" +# password = "corvus-pass" +# token = "..." # preferred over username/password when set +allow_http_loopback = true + [gateway] require_pairing = true # require pairing code on first connect allow_public_bind = false # refuse 0.0.0.0 without tunnel @@ -323,6 +342,19 @@ format = "openclaw" # "openclaw" (default, markdown files) or "aieos # aieos_inline = '{"identity":{"names":{"first":"Nova"}}}' # inline AIEOS JSON ``` +SurrealDB environment overrides (env-first): + +```bash +export CORVUS_MEMORY_BACKEND=surreal +export CORVUS_SURREALDB_URL=http://127.0.0.1:8000 +export CORVUS_SURREALDB_NAMESPACE=corvus +export CORVUS_SURREALDB_DATABASE=memory +export CORVUS_SURREALDB_USERNAME=corvus +export CORVUS_SURREALDB_PASSWORD=your-password-here +# optional: +export CORVUS_SURREALDB_TOKEN=... +``` + ## Identity System (AIEOS Support) Corvus supports **identity-agnostic** AI personas through two formats: @@ -408,7 +440,7 @@ See [aieos.org](https://aieos.org) for the full schema and live examples. | Command | Description | |-----------------------------------------------|---------------------------------------------------------| | `onboard` | Quick setup (default) | -| `onboard --interactive` | Full interactive 7-step wizard | +| `onboard --interactive` | Full interactive 7-step wizard (includes memory backend + Surreal options) | | `onboard --channels-only` | Reconfigure channels/allowlists only (fast repair flow) | | `agent -m "..."` | Single message mode | | `agent` | Interactive chat mode | diff --git a/clients/agent-runtime/docker-compose.yml b/clients/agent-runtime/docker-compose.yml index cb4017d3e..3fbc99c95 100755 --- a/clients/agent-runtime/docker-compose.yml +++ b/clients/agent-runtime/docker-compose.yml @@ -27,6 +27,15 @@ services: # Optional: Model override # - CORVUS_MODEL=anthropic/claude-sonnet-4-20250514 + + # Optional: Memory backend override + # - CORVUS_MEMORY_BACKEND=surreal + # - CORVUS_SURREALDB_URL=http://surrealdb:8000 + # - CORVUS_SURREALDB_NAMESPACE=corvus + # - CORVUS_SURREALDB_DATABASE=memory + # - CORVUS_SURREALDB_USERNAME=corvus + # - CORVUS_SURREALDB_PASSWORD=corvus-pass + # - CORVUS_SURREALDB_TOKEN= volumes: # Persist workspace and config @@ -44,5 +53,13 @@ services: retries: 3 start_period: 10s + # Optional local SurrealDB for memory backend testing/development + surrealdb: + image: surrealdb/surrealdb:v2.3 + command: start --log info --user ${SURREALDB_USER:-corvus} --pass ${SURREALDB_PASS:-corvus-pass} memory + profiles: ["surreal"] + ports: + - "8000:8000" + volumes: corvus-data: diff --git a/clients/agent-runtime/src/config/mod.rs b/clients/agent-runtime/src/config/mod.rs index 227789fd0..06d34b91f 100755 --- a/clients/agent-runtime/src/config/mod.rs +++ b/clients/agent-runtime/src/config/mod.rs @@ -9,8 +9,8 @@ pub use schema::{ LarkConfig, MatrixConfig, MemoryConfig, ModelRouteConfig, ObservabilityConfig, PeripheralBoardConfig, PeripheralsConfig, QueryClassificationConfig, ReliabilityConfig, ResourceLimitsConfig, RuntimeConfig, SandboxBackend, SandboxConfig, SchedulerConfig, - SecretsConfig, SecurityConfig, SlackConfig, StreamMode, TelegramConfig, TunnelConfig, - WebSearchConfig, WebhookConfig, + SecretsConfig, SecurityConfig, SlackConfig, StreamMode, SurrealMemoryConfig, TelegramConfig, + TunnelConfig, WebSearchConfig, WebhookConfig, }; #[cfg(test)] diff --git a/clients/agent-runtime/src/config/schema.rs b/clients/agent-runtime/src/config/schema.rs index 21110eff6..8137aa70e 100755 --- a/clients/agent-runtime/src/config/schema.rs +++ b/clients/agent-runtime/src/config/schema.rs @@ -4,6 +4,7 @@ use anyhow::{Context, Result}; use directories::UserDirs; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use std::fmt; use std::fs::{self, File, OpenOptions}; use std::io::Write; use std::path::{Path, PathBuf}; @@ -771,10 +772,71 @@ impl Default for WebSearchConfig { // ── Memory ─────────────────────────────────────────────────── +#[derive(Clone, Serialize, Deserialize)] +pub struct SurrealMemoryConfig { + /// SurrealDB endpoint URL, e.g. "http://127.0.0.1:8000" or "ws://127.0.0.1:8000/rpc" + #[serde(default)] + pub url: Option, + /// SurrealDB namespace + #[serde(default = "default_surreal_namespace")] + pub namespace: Option, + /// SurrealDB database + #[serde(default = "default_surreal_database")] + pub database: Option, + /// SurrealDB username (ignored if token is provided) + #[serde(default)] + pub username: Option, + /// SurrealDB password (ignored if token is provided) + #[serde(default)] + pub password: Option, + /// SurrealDB auth token (preferred over username/password) + #[serde(default)] + pub token: Option, + /// Allow plain HTTP for loopback addresses only. + #[serde(default = "default_true")] + pub allow_http_loopback: bool, +} + +fn default_surreal_namespace() -> Option { + Some("corvus".to_string()) +} + +fn default_surreal_database() -> Option { + Some("memory".to_string()) +} + +impl Default for SurrealMemoryConfig { + fn default() -> Self { + Self { + url: None, + namespace: default_surreal_namespace(), + database: default_surreal_database(), + username: None, + password: None, + token: None, + allow_http_loopback: true, + } + } +} + +impl fmt::Debug for SurrealMemoryConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SurrealMemoryConfig") + .field("url", &self.url) + .field("namespace", &self.namespace) + .field("database", &self.database) + .field("username", &self.username.as_ref().map(|_| "")) + .field("password", &self.password.as_ref().map(|_| "")) + .field("token", &self.token.as_ref().map(|_| "")) + .field("allow_http_loopback", &self.allow_http_loopback) + .finish() + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[allow(clippy::struct_excessive_bools)] pub struct MemoryConfig { - /// "sqlite" | "lucid" | "markdown" | "none" (`none` = explicit no-op memory) + /// "sqlite" | "lucid" | "markdown" | "surreal" | "none" (`none` = explicit no-op memory) pub backend: String, /// Auto-save conversation context to memory pub auto_save: bool, @@ -844,6 +906,10 @@ pub struct MemoryConfig { /// None = wait indefinitely (default). Recommended max: 300. #[serde(default)] pub sqlite_open_timeout_secs: Option, + + /// SurrealDB backend settings. + #[serde(default)] + pub surreal: SurrealMemoryConfig, } fn default_embedding_provider() -> String { @@ -913,6 +979,7 @@ impl Default for MemoryConfig { snapshot_on_hygiene: false, auto_hydrate: true, sqlite_open_timeout_secs: None, + surreal: SurrealMemoryConfig::default(), } } } @@ -1987,9 +2054,7 @@ fn resolve_config_dir_for_workspace(workspace_dir: &Path) -> (PathBuf, PathBuf) ); } - let legacy_config_dir = workspace_dir - .parent() - .map(|parent| parent.join(".corvus")); + let legacy_config_dir = workspace_dir.parent().map(|parent| parent.join(".corvus")); if let Some(legacy_dir) = legacy_config_dir { if legacy_dir.join("config.toml").exists() { return (legacy_dir, workspace_config_dir); @@ -2043,6 +2108,15 @@ fn encrypt_optional_secret( Ok(()) } +fn env_override_optional(var_name: &str, target: &mut Option) { + if let Ok(raw) = std::env::var(var_name) { + let trimmed = raw.trim(); + if !trimmed.is_empty() { + *target = Some(trimmed.to_string()); + } + } +} + impl Config { pub fn load_or_init() -> Result { let (default_corvus_dir, default_workspace_dir) = default_config_and_workspace_dirs()?; @@ -2108,6 +2182,21 @@ impl Config { &mut config.web_search.brave_api_key, "config.web_search.brave_api_key", )?; + decrypt_optional_secret( + &store, + &mut config.memory.surreal.username, + "config.memory.surreal.username", + )?; + decrypt_optional_secret( + &store, + &mut config.memory.surreal.password, + "config.memory.surreal.password", + )?; + decrypt_optional_secret( + &store, + &mut config.memory.surreal.token, + "config.memory.surreal.token", + )?; for agent in config.agents.values_mut() { decrypt_optional_secret(&store, &mut agent.api_key, "config.agents.*.api_key")?; @@ -2174,6 +2263,27 @@ impl Config { } } + // Memory backend: CORVUS_MEMORY_BACKEND or MEMORY_BACKEND + if let Ok(backend) = + std::env::var("CORVUS_MEMORY_BACKEND").or_else(|_| std::env::var("MEMORY_BACKEND")) + { + let backend_raw = backend.trim(); + if !backend_raw.is_empty() { + let backend = backend_raw.to_ascii_lowercase(); + if matches!( + backend.as_str(), + "sqlite" | "lucid" | "markdown" | "surreal" | "none" + ) { + self.memory.backend = backend; + } else { + tracing::warn!( + "ignoring unknown memory backend override '{}'; allowed: sqlite, lucid, markdown, surreal, none", + backend_raw + ); + } + } + } + // Workspace directory: CORVUS_WORKSPACE if let Ok(workspace) = std::env::var("CORVUS_WORKSPACE") { if !workspace.is_empty() { @@ -2193,8 +2303,7 @@ impl Config { } // Gateway host: CORVUS_GATEWAY_HOST or HOST - if let Ok(host) = std::env::var("CORVUS_GATEWAY_HOST").or_else(|_| std::env::var("HOST")) - { + if let Ok(host) = std::env::var("CORVUS_GATEWAY_HOST").or_else(|_| std::env::var("HOST")) { if !host.is_empty() { self.gateway.host = host; } @@ -2262,6 +2371,25 @@ impl Config { } } } + + env_override_optional("CORVUS_SURREALDB_URL", &mut self.memory.surreal.url); + env_override_optional( + "CORVUS_SURREALDB_NAMESPACE", + &mut self.memory.surreal.namespace, + ); + env_override_optional( + "CORVUS_SURREALDB_DATABASE", + &mut self.memory.surreal.database, + ); + env_override_optional( + "CORVUS_SURREALDB_USERNAME", + &mut self.memory.surreal.username, + ); + env_override_optional( + "CORVUS_SURREALDB_PASSWORD", + &mut self.memory.surreal.password, + ); + env_override_optional("CORVUS_SURREALDB_TOKEN", &mut self.memory.surreal.token); } pub fn save(&self) -> Result<()> { @@ -2291,6 +2419,21 @@ impl Config { &mut config_to_save.web_search.brave_api_key, "config.web_search.brave_api_key", )?; + encrypt_optional_secret( + &store, + &mut config_to_save.memory.surreal.username, + "config.memory.surreal.username", + )?; + encrypt_optional_secret( + &store, + &mut config_to_save.memory.surreal.password, + "config.memory.surreal.password", + )?; + encrypt_optional_secret( + &store, + &mut config_to_save.memory.surreal.token, + "config.memory.surreal.token", + )?; for agent in config_to_save.agents.values_mut() { encrypt_optional_secret(&store, &mut agent.api_key, "config.agents.*.api_key")?; @@ -2477,6 +2620,27 @@ default_temperature = 0.7 assert_eq!(m.purge_after_days, 30); assert_eq!(m.conversation_retention_days, 30); assert!(m.sqlite_open_timeout_secs.is_none()); + assert_eq!(m.surreal.namespace.as_deref(), Some("corvus")); + assert_eq!(m.surreal.database.as_deref(), Some("memory")); + assert!(m.surreal.allow_http_loopback); + } + + #[test] + fn surreal_memory_config_debug_redacts_sensitive_fields() { + let cfg = SurrealMemoryConfig { + url: Some("http://127.0.0.1:8000".into()), + namespace: Some("corvus".into()), + database: Some("memory".into()), + username: Some("svc-user".into()), + password: Some("secret-pass".into()), + token: Some("secret-token".into()), + allow_http_loopback: true, + }; + let rendered = format!("{cfg:?}"); + assert!(rendered.contains("")); + assert!(!rendered.contains("svc-user")); + assert!(!rendered.contains("secret-pass")); + assert!(!rendered.contains("secret-token")); } #[test] @@ -2710,6 +2874,12 @@ tool_dispatcher = "xml" config.composio.api_key = Some("composio-credential".into()); config.browser.computer_use.api_key = Some("browser-credential".into()); config.web_search.brave_api_key = Some("brave-credential".into()); + config.memory.surreal.url = Some("http://127.0.0.1:8000".into()); + config.memory.surreal.namespace = Some("test-ns".into()); + config.memory.surreal.database = Some("test-db".into()); + config.memory.surreal.username = Some("test-user".into()); + config.memory.surreal.password = Some("test-pass".into()); + config.memory.surreal.token = Some("test-token".into()); config.agents.insert( "worker".into(), @@ -2760,6 +2930,43 @@ tool_dispatcher = "xml" "brave-credential" ); + let surreal_url = stored.memory.surreal.url.as_deref().unwrap(); + assert!(!crate::security::SecretStore::is_encrypted(surreal_url)); + assert_eq!(surreal_url, "http://127.0.0.1:8000"); + + let surreal_namespace = stored.memory.surreal.namespace.as_deref().unwrap(); + assert!(!crate::security::SecretStore::is_encrypted( + surreal_namespace + )); + assert_eq!(surreal_namespace, "test-ns"); + + let surreal_database = stored.memory.surreal.database.as_deref().unwrap(); + assert!(!crate::security::SecretStore::is_encrypted( + surreal_database + )); + assert_eq!(surreal_database, "test-db"); + + let surreal_user_encrypted = stored.memory.surreal.username.as_deref().unwrap(); + assert!(crate::security::SecretStore::is_encrypted( + surreal_user_encrypted + )); + assert_eq!(store.decrypt(surreal_user_encrypted).unwrap(), "test-user"); + + let surreal_pass_encrypted = stored.memory.surreal.password.as_deref().unwrap(); + assert!(crate::security::SecretStore::is_encrypted( + surreal_pass_encrypted + )); + assert_eq!(store.decrypt(surreal_pass_encrypted).unwrap(), "test-pass"); + + let surreal_token_encrypted = stored.memory.surreal.token.as_deref().unwrap(); + assert!(crate::security::SecretStore::is_encrypted( + surreal_token_encrypted + )); + assert_eq!( + store.decrypt(surreal_token_encrypted).unwrap(), + "test-token" + ); + let worker = stored.agents.get("worker").unwrap(); let worker_encrypted = worker.api_key.as_deref().unwrap(); assert!(crate::security::SecretStore::is_encrypted(worker_encrypted)); @@ -2770,8 +2977,7 @@ tool_dispatcher = "xml" #[test] fn config_save_atomic_cleanup() { - let dir = - std::env::temp_dir().join(format!("corvus_test_config_{}", uuid::Uuid::new_v4())); + let dir = std::env::temp_dir().join(format!("corvus_test_config_{}", uuid::Uuid::new_v4())); fs::create_dir_all(&dir).unwrap(); let config_path = dir.join("config.toml"); @@ -3567,6 +3773,46 @@ default_temperature = 0.7 std::env::remove_var("MODEL"); } + #[test] + fn env_override_memory_backend() { + let _env_guard = env_override_test_guard(); + let mut config = Config::default(); + assert_eq!(config.memory.backend, "sqlite"); + + std::env::set_var("CORVUS_MEMORY_BACKEND", "surreal"); + config.apply_env_overrides(); + assert_eq!(config.memory.backend, "surreal"); + + std::env::remove_var("CORVUS_MEMORY_BACKEND"); + } + + #[test] + fn env_override_memory_backend_fallback() { + let _env_guard = env_override_test_guard(); + let mut config = Config::default(); + assert_eq!(config.memory.backend, "sqlite"); + + std::env::remove_var("CORVUS_MEMORY_BACKEND"); + std::env::set_var("MEMORY_BACKEND", "markdown"); + config.apply_env_overrides(); + assert_eq!(config.memory.backend, "markdown"); + + std::env::remove_var("MEMORY_BACKEND"); + } + + #[test] + fn env_override_memory_backend_invalid_ignored() { + let _env_guard = env_override_test_guard(); + let mut config = Config::default(); + assert_eq!(config.memory.backend, "sqlite"); + + std::env::set_var("CORVUS_MEMORY_BACKEND", "unsupported"); + config.apply_env_overrides(); + assert_eq!(config.memory.backend, "sqlite"); + + std::env::remove_var("CORVUS_MEMORY_BACKEND"); + } + #[test] fn env_override_workspace() { let _env_guard = env_override_test_guard(); @@ -3921,6 +4167,38 @@ default_model = "legacy-model" std::env::remove_var("WEB_SEARCH_TIMEOUT_SECS"); } + #[test] + fn env_override_surreal_memory_config() { + let _env_guard = env_override_test_guard(); + let mut config = Config::default(); + + std::env::set_var("CORVUS_SURREALDB_URL", "https://db.example.com"); + std::env::set_var("CORVUS_SURREALDB_NAMESPACE", "prod-ns"); + std::env::set_var("CORVUS_SURREALDB_DATABASE", "prod-db"); + std::env::set_var("CORVUS_SURREALDB_USERNAME", "svc-user"); + std::env::set_var("CORVUS_SURREALDB_PASSWORD", "svc-pass"); + std::env::set_var("CORVUS_SURREALDB_TOKEN", "svc-token"); + + config.apply_env_overrides(); + + assert_eq!( + config.memory.surreal.url.as_deref(), + Some("https://db.example.com") + ); + assert_eq!(config.memory.surreal.namespace.as_deref(), Some("prod-ns")); + assert_eq!(config.memory.surreal.database.as_deref(), Some("prod-db")); + assert_eq!(config.memory.surreal.username.as_deref(), Some("svc-user")); + assert_eq!(config.memory.surreal.password.as_deref(), Some("svc-pass")); + assert_eq!(config.memory.surreal.token.as_deref(), Some("svc-token")); + + std::env::remove_var("CORVUS_SURREALDB_URL"); + std::env::remove_var("CORVUS_SURREALDB_NAMESPACE"); + std::env::remove_var("CORVUS_SURREALDB_DATABASE"); + std::env::remove_var("CORVUS_SURREALDB_USERNAME"); + std::env::remove_var("CORVUS_SURREALDB_PASSWORD"); + std::env::remove_var("CORVUS_SURREALDB_TOKEN"); + } + #[test] fn gateway_config_default_values() { let g = GatewayConfig::default(); diff --git a/clients/agent-runtime/src/main.rs b/clients/agent-runtime/src/main.rs index 8ff5c38d7..552aaaf21 100755 --- a/clients/agent-runtime/src/main.rs +++ b/clients/agent-runtime/src/main.rs @@ -121,7 +121,7 @@ enum Commands { #[arg(long)] provider: Option, - /// Memory backend (sqlite, lucid, markdown, none) - used in quick mode, default: sqlite + /// Memory backend (sqlite, lucid, surreal, markdown, none) - used in quick mode (default: sqlite) #[arg(long)] memory: Option, }, diff --git a/clients/agent-runtime/src/memory/backend.rs b/clients/agent-runtime/src/memory/backend.rs index 8ba7ec395..c0b8cafbd 100755 --- a/clients/agent-runtime/src/memory/backend.rs +++ b/clients/agent-runtime/src/memory/backend.rs @@ -2,6 +2,7 @@ pub enum MemoryBackendKind { Sqlite, Lucid, + Surreal, Markdown, None, Unknown, @@ -45,6 +46,15 @@ const MARKDOWN_PROFILE: MemoryBackendProfile = MemoryBackendProfile { optional_dependency: false, }; +const SURREAL_PROFILE: MemoryBackendProfile = MemoryBackendProfile { + key: "surreal", + label: "SurrealDB (Knowledge Graph + Episodic + Vector) — remote persistent memory", + auto_save_default: true, + uses_sqlite_hygiene: true, + sqlite_based: false, + optional_dependency: true, +}; + const NONE_PROFILE: MemoryBackendProfile = MemoryBackendProfile { key: "none", label: "None — disable persistent memory", @@ -63,6 +73,16 @@ const CUSTOM_PROFILE: MemoryBackendProfile = MemoryBackendProfile { optional_dependency: false, }; +#[cfg(feature = "memory-surreal")] +const SELECTABLE_MEMORY_BACKENDS: [MemoryBackendProfile; 5] = [ + SQLITE_PROFILE, + LUCID_PROFILE, + SURREAL_PROFILE, + MARKDOWN_PROFILE, + NONE_PROFILE, +]; + +#[cfg(not(feature = "memory-surreal"))] const SELECTABLE_MEMORY_BACKENDS: [MemoryBackendProfile; 4] = [ SQLITE_PROFILE, LUCID_PROFILE, @@ -82,6 +102,7 @@ pub fn classify_memory_backend(backend: &str) -> MemoryBackendKind { match backend { "sqlite" => MemoryBackendKind::Sqlite, "lucid" => MemoryBackendKind::Lucid, + "surreal" => MemoryBackendKind::Surreal, "markdown" => MemoryBackendKind::Markdown, "none" => MemoryBackendKind::None, _ => MemoryBackendKind::Unknown, @@ -92,6 +113,7 @@ pub fn memory_backend_profile(backend: &str) -> MemoryBackendProfile { match classify_memory_backend(backend) { MemoryBackendKind::Sqlite => SQLITE_PROFILE, MemoryBackendKind::Lucid => LUCID_PROFILE, + MemoryBackendKind::Surreal => SURREAL_PROFILE, MemoryBackendKind::Markdown => MARKDOWN_PROFILE, MemoryBackendKind::None => NONE_PROFILE, MemoryBackendKind::Unknown => CUSTOM_PROFILE, @@ -106,6 +128,10 @@ mod tests { fn classify_known_backends() { assert_eq!(classify_memory_backend("sqlite"), MemoryBackendKind::Sqlite); assert_eq!(classify_memory_backend("lucid"), MemoryBackendKind::Lucid); + assert_eq!( + classify_memory_backend("surreal"), + MemoryBackendKind::Surreal + ); assert_eq!( classify_memory_backend("markdown"), MemoryBackendKind::Markdown @@ -121,11 +147,23 @@ mod tests { #[test] fn selectable_backends_are_ordered_for_onboarding() { let backends = selectable_memory_backends(); + #[cfg(feature = "memory-surreal")] + assert_eq!(backends.len(), 5); + #[cfg(not(feature = "memory-surreal"))] assert_eq!(backends.len(), 4); assert_eq!(backends[0].key, "sqlite"); assert_eq!(backends[1].key, "lucid"); - assert_eq!(backends[2].key, "markdown"); - assert_eq!(backends[3].key, "none"); + #[cfg(feature = "memory-surreal")] + { + assert_eq!(backends[2].key, "surreal"); + assert_eq!(backends[3].key, "markdown"); + assert_eq!(backends[4].key, "none"); + } + #[cfg(not(feature = "memory-surreal"))] + { + assert_eq!(backends[2].key, "markdown"); + assert_eq!(backends[3].key, "none"); + } } #[test] @@ -136,6 +174,14 @@ mod tests { assert!(profile.uses_sqlite_hygiene); } + #[test] + fn surreal_profile_is_non_sqlite_optional_backend() { + let profile = memory_backend_profile("surreal"); + assert!(!profile.sqlite_based); + assert!(profile.optional_dependency); + assert!(profile.uses_sqlite_hygiene); + } + #[test] fn unknown_profile_preserves_extensibility_defaults() { let profile = memory_backend_profile("custom-memory"); diff --git a/clients/agent-runtime/src/memory/mod.rs b/clients/agent-runtime/src/memory/mod.rs index 6798ee433..2ab105ccf 100755 --- a/clients/agent-runtime/src/memory/mod.rs +++ b/clients/agent-runtime/src/memory/mod.rs @@ -8,6 +8,8 @@ pub mod none; pub mod response_cache; pub mod snapshot; pub mod sqlite; +#[cfg(feature = "memory-surreal")] +pub mod surreal; pub mod traits; pub mod vector; @@ -21,6 +23,8 @@ pub use markdown::MarkdownMemory; pub use none::NoneMemory; pub use response_cache::ResponseCache; pub use sqlite::SqliteMemory; +#[cfg(feature = "memory-surreal")] +pub use surreal::SurrealMemory; pub use traits::Memory; #[allow(unused_imports)] pub use traits::{MemoryCategory, MemoryEntry}; @@ -29,30 +33,29 @@ use crate::config::MemoryConfig; use std::path::Path; use std::sync::Arc; -fn create_memory_with_sqlite_builder( - backend_name: &str, +fn build_sqlite_memory( + config: &MemoryConfig, workspace_dir: &Path, - mut sqlite_builder: F, - unknown_context: &str, -) -> anyhow::Result> -where - F: FnMut() -> anyhow::Result, -{ - match classify_memory_backend(backend_name) { - MemoryBackendKind::Sqlite => Ok(Box::new(sqlite_builder()?)), - MemoryBackendKind::Lucid => { - let local = sqlite_builder()?; - Ok(Box::new(LucidMemory::new(workspace_dir, local))) - } - MemoryBackendKind::Markdown => Ok(Box::new(MarkdownMemory::new(workspace_dir))), - MemoryBackendKind::None => Ok(Box::new(NoneMemory::new())), - MemoryBackendKind::Unknown => { - tracing::warn!( - "Unknown memory backend '{backend_name}'{unknown_context}, falling back to markdown" - ); - Ok(Box::new(MarkdownMemory::new(workspace_dir))) - } - } + api_key: Option<&str>, +) -> anyhow::Result { + let embedder: Arc = + Arc::from(embeddings::create_embedding_provider( + &config.embedding_provider, + api_key, + &config.embedding_model, + config.embedding_dimensions, + )); + + #[allow(clippy::cast_possible_truncation)] + let mem = SqliteMemory::with_embedder( + workspace_dir, + embedder, + config.vector_weight as f32, + config.keyword_weight as f32, + config.embedding_cache_size, + config.sqlite_open_timeout_secs, + )?; + Ok(mem) } /// Factory: create the right memory backend from config @@ -95,37 +98,54 @@ pub fn create_memory( } } - fn build_sqlite_memory( - config: &MemoryConfig, - workspace_dir: &Path, - api_key: Option<&str>, - ) -> anyhow::Result { - let embedder: Arc = - Arc::from(embeddings::create_embedding_provider( - &config.embedding_provider, - api_key, - &config.embedding_model, - config.embedding_dimensions, - )); - - #[allow(clippy::cast_possible_truncation)] - let mem = SqliteMemory::with_embedder( + match classify_memory_backend(&config.backend) { + MemoryBackendKind::Sqlite => Ok(Box::new(build_sqlite_memory( + config, workspace_dir, - embedder, - config.vector_weight as f32, - config.keyword_weight as f32, - config.embedding_cache_size, - config.sqlite_open_timeout_secs, - )?; - Ok(mem) + api_key, + )?)), + MemoryBackendKind::Lucid => { + let local = build_sqlite_memory(config, workspace_dir, api_key)?; + Ok(Box::new(LucidMemory::new(workspace_dir, local))) + } + MemoryBackendKind::Surreal => { + #[cfg(feature = "memory-surreal")] + { + let embedder: Arc = + Arc::from(embeddings::create_embedding_provider( + &config.embedding_provider, + api_key, + &config.embedding_model, + config.embedding_dimensions, + )); + #[allow(clippy::cast_possible_truncation)] + return Ok(Box::new(SurrealMemory::new( + workspace_dir, + config, + embedder, + config.vector_weight as f32, + config.keyword_weight as f32, + )?)); + } + #[cfg(not(feature = "memory-surreal"))] + { + tracing::warn!( + "Memory backend 'surreal' requested but binary was built without \ + feature 'memory-surreal'; falling back to markdown" + ); + Ok(Box::new(MarkdownMemory::new(workspace_dir))) + } + } + MemoryBackendKind::Markdown => Ok(Box::new(MarkdownMemory::new(workspace_dir))), + MemoryBackendKind::None => Ok(Box::new(NoneMemory::new())), + MemoryBackendKind::Unknown => { + tracing::warn!( + "Unknown memory backend '{}', falling back to markdown", + config.backend + ); + Ok(Box::new(MarkdownMemory::new(workspace_dir))) + } } - - create_memory_with_sqlite_builder( - &config.backend, - workspace_dir, - || build_sqlite_memory(config, workspace_dir, api_key), - "", - ) } pub fn create_memory_for_migration( @@ -134,16 +154,46 @@ pub fn create_memory_for_migration( ) -> anyhow::Result> { if matches!(classify_memory_backend(backend), MemoryBackendKind::None) { anyhow::bail!( - "memory backend 'none' disables persistence; choose sqlite, lucid, or markdown before migration" + "memory backend 'none' disables persistence; choose sqlite, lucid, surreal, or markdown before migration" ); } - create_memory_with_sqlite_builder( - backend, - workspace_dir, - || SqliteMemory::new(workspace_dir), - " during migration", - ) + match classify_memory_backend(backend) { + MemoryBackendKind::Sqlite => Ok(Box::new(SqliteMemory::new(workspace_dir)?)), + MemoryBackendKind::Lucid => { + let local = SqliteMemory::new(workspace_dir)?; + Ok(Box::new(LucidMemory::new(workspace_dir, local))) + } + MemoryBackendKind::Surreal => { + #[cfg(feature = "memory-surreal")] + #[allow(clippy::cast_possible_truncation)] + { + let config = MemoryConfig { + backend: "surreal".to_string(), + ..MemoryConfig::default() + }; + let embedder: Arc = + Arc::new(embeddings::NoopEmbedding); + return Ok(Box::new(SurrealMemory::new( + workspace_dir, + &config, + embedder, + config.vector_weight as f32, + config.keyword_weight as f32, + )?)); + } + #[cfg(not(feature = "memory-surreal"))] + { + anyhow::bail!( + "backend 'surreal' requires the binary to be built with feature 'memory-surreal'" + ); + } + } + MemoryBackendKind::Markdown | MemoryBackendKind::Unknown => { + Ok(Box::new(MarkdownMemory::new(workspace_dir))) + } + MemoryBackendKind::None => unreachable!("checked above"), + } } /// Factory: create an optional response cache from config. @@ -232,6 +282,30 @@ mod tests { assert_eq!(mem.name(), "markdown"); } + #[cfg(not(feature = "memory-surreal"))] + #[test] + fn factory_surreal_without_feature_falls_back_to_markdown() { + let tmp = TempDir::new().unwrap(); + let cfg = MemoryConfig { + backend: "surreal".into(), + ..MemoryConfig::default() + }; + let mem = create_memory(&cfg, tmp.path(), None).unwrap(); + assert_eq!(mem.name(), "markdown"); + } + + #[cfg(feature = "memory-surreal")] + #[test] + fn factory_surreal_with_feature_uses_surreal_backend() { + let tmp = TempDir::new().unwrap(); + let cfg = MemoryConfig { + backend: "surreal".into(), + ..MemoryConfig::default() + }; + let mem = create_memory(&cfg, tmp.path(), None).unwrap(); + assert_eq!(mem.name(), "surreal"); + } + #[test] fn migration_factory_lucid() { let tmp = TempDir::new().unwrap(); @@ -247,4 +321,14 @@ mod tests { .expect("backend=none should be rejected for migration"); assert!(error.to_string().contains("disables persistence")); } + + #[cfg(not(feature = "memory-surreal"))] + #[test] + fn migration_surreal_requires_feature() { + let tmp = TempDir::new().unwrap(); + let error = create_memory_for_migration("surreal", tmp.path()) + .err() + .expect("surreal should require memory-surreal feature"); + assert!(error.to_string().contains("memory-surreal")); + } } diff --git a/clients/agent-runtime/src/memory/surreal.rs b/clients/agent-runtime/src/memory/surreal.rs new file mode 100644 index 000000000..a46a961fe --- /dev/null +++ b/clients/agent-runtime/src/memory/surreal.rs @@ -0,0 +1,763 @@ +use super::embeddings::EmbeddingProvider; +use super::traits::{Memory, MemoryCategory, MemoryEntry}; +use super::vector; +use crate::config::MemoryConfig; +use anyhow::{Context, Result}; +use async_trait::async_trait; +use chrono::Local; +use reqwest::Url; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::collections::HashMap; +use std::path::Path; +use std::sync::Arc; +use surrealdb::engine::remote::ws::{Client, Ws}; +use surrealdb::opt::auth::Root; +use surrealdb::sql::Thing; +use surrealdb::Surreal; +use tokio::sync::OnceCell; + +pub struct SurrealMemory { + client: OnceCell>, + ws_endpoint: String, + namespace: String, + database: String, + username: Option, + password: Option, + token: Option, + embedder: Arc, + vector_weight: f32, + keyword_weight: f32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct EntryWrite { + key: String, + content: String, + category: String, + timestamp: String, + session_id: Option, + embedding: Option>, + updated_at: String, +} + +#[derive(Debug, Clone, Deserialize)] +struct EntryRow { + id: Thing, + key: String, + content: String, + category: String, + timestamp: String, + #[serde(default)] + session_id: Option, + #[serde(default)] + embedding: Option>, + #[serde(default)] + updated_at: Option, +} + +impl SurrealMemory { + pub fn new( + _workspace_dir: &Path, + config: &MemoryConfig, + embedder: Arc, + vector_weight: f32, + keyword_weight: f32, + ) -> Result { + let raw_endpoint = config + .surreal + .url + .clone() + .unwrap_or_else(|| "http://127.0.0.1:8000".to_string()); + let endpoint = parse_endpoint(&raw_endpoint)?; + validate_endpoint_security(&endpoint, config.surreal.allow_http_loopback)?; + + let ws_endpoint = normalize_ws_endpoint(endpoint)?; + let namespace = non_empty_or_default(config.surreal.namespace.clone(), "corvus"); + let database = non_empty_or_default(config.surreal.database.clone(), "memory"); + let username = trim_optional(config.surreal.username.clone()); + let password = trim_optional(config.surreal.password.clone()); + let token = trim_optional(config.surreal.token.clone()); + + if username.as_deref() == Some("root") && !is_loopback_host_from_endpoint(&ws_endpoint) { + tracing::warn!( + "SurrealDB backend is configured with root credentials on a non-loopback host" + ); + } + + Ok(Self { + client: OnceCell::new(), + ws_endpoint, + namespace, + database, + username, + password, + token, + embedder, + vector_weight, + keyword_weight, + }) + } + + async fn client(&self) -> Result<&Surreal> { + self.client + .get_or_try_init(|| { + let ws_endpoint = self.ws_endpoint.clone(); + let namespace = self.namespace.clone(); + let database = self.database.clone(); + let username = self.username.clone(); + let password = self.password.clone(); + let token = self.token.clone(); + + async move { + let db = Surreal::new::(ws_endpoint.as_str()) + .await + .context("failed to connect to SurrealDB")?; + + if let Some(jwt) = token { + db.authenticate(jwt) + .await + .context("failed SurrealDB token authentication")?; + } else if let (Some(user), Some(pass)) = (username, password) { + db.signin(Root { + username: &user, + password: &pass, + }) + .await + .context("failed SurrealDB username/password authentication")?; + } + + db.use_ns(namespace) + .use_db(database) + .await + .context("failed selecting SurrealDB namespace/database")?; + + Self::ensure_schema(&db).await?; + Ok(db) + } + }) + .await + } + + async fn ensure_schema(db: &Surreal) -> Result<()> { + let statements = [ + "DEFINE TABLE memory_entries SCHEMALESS;", + "DEFINE TABLE memory_events SCHEMALESS;", + "DEFINE TABLE memory_relations SCHEMALESS;", + "DEFINE INDEX idx_memory_entries_key ON TABLE memory_entries COLUMNS key UNIQUE;", + "DEFINE INDEX idx_memory_entries_category ON TABLE memory_entries COLUMNS category;", + "DEFINE INDEX idx_memory_entries_session ON TABLE memory_entries COLUMNS session_id;", + "DEFINE INDEX idx_memory_entries_updated ON TABLE memory_entries COLUMNS updated_at;", + ]; + + for statement in statements { + if let Err(error) = db.query(statement).await { + let message = error.to_string(); + if message.to_ascii_lowercase().contains("already exists") { + tracing::debug!("SurrealDB schema statement already exists: {statement}"); + continue; + } + tracing::warn!("SurrealDB schema statement failed: {statement}; error: {message}"); + return Err(error).context("failed applying SurrealDB schema"); + } + } + + Ok(()) + } + + fn category_to_str(category: &MemoryCategory) -> String { + match category { + MemoryCategory::Core => "core".to_string(), + MemoryCategory::Daily => "daily".to_string(), + MemoryCategory::Conversation => "conversation".to_string(), + MemoryCategory::Custom(value) => value.clone(), + } + } + + fn str_to_category(value: &str) -> MemoryCategory { + match value { + "core" => MemoryCategory::Core, + "daily" => MemoryCategory::Daily, + "conversation" => MemoryCategory::Conversation, + custom => MemoryCategory::Custom(custom.to_string()), + } + } + + fn record_id_for_key(key: &str) -> String { + let digest = Sha256::digest(key.as_bytes()); + hex::encode(digest) + } + + fn row_to_entry(row: EntryRow, score: Option) -> MemoryEntry { + MemoryEntry { + id: row.id.to_string(), + key: row.key, + content: row.content, + category: Self::str_to_category(&row.category), + timestamp: row.updated_at.unwrap_or(row.timestamp), + session_id: row.session_id, + score, + } + } + + async fn list_rows( + &self, + category: Option<&MemoryCategory>, + session_id: Option<&str>, + limit: usize, + ) -> Result> { + let db = self.client().await?; + let mut query = "SELECT * FROM memory_entries".to_string(); + let mut clauses = Vec::new(); + if category.is_some() { + clauses.push("category = $category"); + } + if session_id.is_some() { + clauses.push("session_id = $session_id"); + } + if !clauses.is_empty() { + query.push_str(" WHERE "); + query.push_str(&clauses.join(" AND ")); + } + query.push_str(" ORDER BY updated_at DESC LIMIT $limit;"); + + let mut request = db.query(query).bind(("limit", limit)); + if let Some(value) = category { + request = request.bind(("category", Self::category_to_str(value))); + } + if let Some(sid) = session_id { + request = request.bind(("session_id", sid.to_string())); + } + + let mut response = request.await.context("failed to list SurrealDB entries")?; + let rows: Vec = response + .take(0) + .context("failed to decode SurrealDB entries")?; + Ok(rows) + } + + async fn recall_rows( + &self, + query_lower: &str, + session_id: Option<&str>, + ) -> Result> { + let db = self.client().await?; + let mut request = db.query( + "SELECT * FROM memory_entries + WHERE ( + string::contains(string::lowercase(key), $query) + OR string::contains(string::lowercase(content), $query) + ) + AND ($session_id = NONE OR session_id = $session_id) + ORDER BY updated_at DESC + LIMIT 500;", + ); + request = request.bind(("query", query_lower.to_string())); + request = request.bind(("session_id", session_id.map(str::to_string))); + + let mut response = request + .await + .context("failed to recall SurrealDB entries")?; + let rows: Vec = response + .take(0) + .context("failed to decode SurrealDB recall entries")?; + Ok(rows) + } + + async fn count_rows(&self) -> Result { + #[derive(Debug, Deserialize)] + struct CountRow { + count: usize, + } + + let db = self.client().await?; + let mut response = db + .query("SELECT count() AS count FROM memory_entries GROUP ALL;") + .await + .context("failed to count SurrealDB entries")?; + let rows: Vec = response + .take(0) + .context("failed to decode SurrealDB count result")?; + Ok(rows.first().map_or(0, |row| row.count)) + } + + async fn log_event( + &self, + action: &str, + entry_id: &str, + key: &str, + category: &MemoryCategory, + session_id: Option<&str>, + ) -> Result<()> { + let db = self.client().await?; + db.query( + "CREATE memory_events CONTENT { + action: $action, + entry_id: $entry_id, + key: $key, + category: $category, + session_id: $session_id, + at: $at + };", + ) + .bind(("action", action.to_string())) + .bind(("entry_id", entry_id.to_string())) + .bind(("key", key.to_string())) + .bind(("category", Self::category_to_str(category))) + .bind(("session_id", session_id.map(str::to_string))) + .bind(("at", Local::now().to_rfc3339())) + .await + .context("failed to log SurrealDB episodic event")?; + Ok(()) + } +} + +#[async_trait] +impl Memory for SurrealMemory { + fn name(&self) -> &str { + "surreal" + } + + async fn store( + &self, + key: &str, + content: &str, + category: MemoryCategory, + session_id: Option<&str>, + ) -> Result<()> { + if key.trim().is_empty() { + anyhow::bail!("memory key cannot be empty"); + } + if content.trim().is_empty() { + anyhow::bail!("memory content cannot be empty"); + } + + let record_id = Self::record_id_for_key(key); + let db = self.client().await?; + + let embedding = if self.embedder.dimensions() > 0 { + Some( + self.embedder + .embed_one(content) + .await + .context("failed generating embedding for SurrealDB memory")?, + ) + } else { + None + }; + + let now = Local::now().to_rfc3339(); + let payload = EntryWrite { + key: key.to_string(), + content: content.to_string(), + category: Self::category_to_str(&category), + timestamp: now.clone(), + session_id: session_id.map(str::to_string), + embedding, + updated_at: now, + }; + + let category_value = Self::category_to_str(&category); + let category_node = format!("category:{category_value}"); + let session_node = session_id.map(|sid| format!("session:{sid}")); + + let mut tx = db.query( + "BEGIN TRANSACTION; + LET $existing = SELECT id FROM memory_entries WHERE key = $key LIMIT 1; + LET $action = IF array::len($existing) > 0 THEN \"update\" ELSE \"store\" END; + UPSERT type::thing(\"memory_entries\", $record_id) CONTENT $payload; + CREATE memory_events CONTENT { + action: $action, + entry_id: $record_id, + key: $key, + category: $category, + session_id: $session_id, + at: $at + }; + CREATE memory_relations CONTENT { + relation_type: \"entry_category\", + from_entry_id: $record_id, + to_node_id: $category_node, + session_id: $session_id, + at: $at + }; + IF $session_id != NONE THEN ( + CREATE memory_relations CONTENT { + relation_type: \"entry_session\", + from_entry_id: $record_id, + to_node_id: $session_node, + session_id: $session_id, + at: $at + }; + LET $previous = SELECT id FROM memory_entries + WHERE session_id = $session_id AND key != $key + ORDER BY updated_at DESC + LIMIT 1; + IF array::len($previous) > 0 THEN ( + CREATE memory_relations CONTENT { + relation_type: \"entry_previous\", + from_entry_id: $record_id, + to_node_id: string::concat(\"entry:\", $previous[0].id), + session_id: $session_id, + at: $at + }; + ); + ); + COMMIT TRANSACTION;", + ); + tx = tx.bind(("key", key.to_string())); + tx = tx.bind(("record_id", record_id.clone())); + tx = tx.bind(("payload", payload)); + tx = tx.bind(("category", category_value)); + tx = tx.bind(("session_id", session_id.map(str::to_string))); + tx = tx.bind(("at", Local::now().to_rfc3339())); + tx = tx.bind(("category_node", category_node)); + tx = tx.bind(("session_node", session_node)); + tx.await.context("failed transactional SurrealDB store")?; + + Ok(()) + } + + async fn recall( + &self, + query: &str, + limit: usize, + session_id: Option<&str>, + ) -> Result> { + let query = query.trim(); + if query.is_empty() || limit == 0 { + return Ok(Vec::new()); + } + + let query_lower = query.to_lowercase(); + let terms: Vec<&str> = query_lower + .split_whitespace() + .filter(|term| !term.is_empty()) + .collect(); + if terms.is_empty() { + return Ok(Vec::new()); + } + + let keyword_candidates = self.recall_rows(&query_lower, session_id).await?; + let vector_candidates = self.list_rows(None, session_id, 500).await?; + let mut candidate_by_id: HashMap = + HashMap::with_capacity(keyword_candidates.len() + vector_candidates.len()); + for row in keyword_candidates.into_iter().chain(vector_candidates) { + let id = row.id.to_string(); + candidate_by_id.entry(id).or_insert(row); + } + + let mut vector_results: Vec<(String, f32)> = Vec::new(); + let mut keyword_results: Vec<(String, f32)> = Vec::new(); + let mut row_by_id: HashMap = + HashMap::with_capacity(candidate_by_id.len()); + let mut searchable_by_id: HashMap = + HashMap::with_capacity(candidate_by_id.len()); + + let query_embedding = if self.embedder.dimensions() > 0 { + Some( + self.embedder + .embed_one(query) + .await + .context("failed generating recall embedding for SurrealDB")?, + ) + } else { + None + }; + + for mut row in candidate_by_id.into_values() { + let id = row.id.to_string(); + let searchable = format!("{} {}", row.key.to_lowercase(), row.content.to_lowercase()); + + let keyword_matches = terms + .iter() + .filter(|term| searchable.contains(**term)) + .count(); + if keyword_matches > 0 { + #[allow(clippy::cast_possible_truncation)] + let mut score = keyword_matches as f32 / terms.len() as f32; + if searchable.contains(&query_lower) { + score = (score + 0.2).min(1.0); + } + keyword_results.push((id.clone(), score)); + } + + if let Some(ref qv) = query_embedding { + if row.embedding.is_none() { + match self.embedder.embed_one(&row.content).await { + Ok(generated) => { + row.embedding = Some(generated); + } + Err(error) => { + tracing::debug!( + "failed generating candidate embedding in surreal recall for key '{}': {error}", + row.key + ); + } + } + } + if let Some(ev) = row.embedding.as_ref() { + let sim = vector::cosine_similarity(qv, ev); + if sim > 0.0 { + vector_results.push((id.clone(), sim)); + } + } + } + + searchable_by_id.insert(id.clone(), searchable); + row_by_id.insert(id, row); + } + + let merged = vector::hybrid_merge( + &vector_results, + &keyword_results, + self.vector_weight, + self.keyword_weight, + limit, + ); + + if merged.is_empty() { + let mut fallback = Vec::new(); + for (id, row) in row_by_id { + if searchable_by_id + .get(&id) + .is_some_and(|searchable| searchable.contains(&query_lower)) + { + fallback.push(Self::row_to_entry(row, Some(1.0))); + if fallback.len() >= limit { + break; + } + } + } + return Ok(fallback); + } + + let mut output = Vec::with_capacity(merged.len()); + for item in merged { + if let Some(row) = row_by_id.remove(&item.id) { + output.push(Self::row_to_entry(row, Some(f64::from(item.final_score)))); + } + } + Ok(output) + } + + async fn get(&self, key: &str) -> Result> { + let db = self.client().await?; + let record_id = Self::record_id_for_key(key); + + let row: Option = db + .select(("memory_entries", record_id.as_str())) + .await + .context("failed selecting SurrealDB memory entry")?; + Ok(row.map(|value| Self::row_to_entry(value, None))) + } + + async fn list( + &self, + category: Option<&MemoryCategory>, + session_id: Option<&str>, + ) -> Result> { + let rows = self.list_rows(category, session_id, 1_000).await?; + let entries = rows + .into_iter() + .map(|row| Self::row_to_entry(row, None)) + .collect(); + Ok(entries) + } + + async fn forget(&self, key: &str) -> Result { + let db = self.client().await?; + let record_id = Self::record_id_for_key(key); + + let deleted: Option = db + .delete(("memory_entries", record_id.as_str())) + .await + .context("failed deleting SurrealDB memory entry")?; + + if deleted.is_some() { + self.log_event( + "forget", + &record_id, + key, + &MemoryCategory::Custom("deleted".into()), + None, + ) + .await?; + } + + Ok(deleted.is_some()) + } + + async fn count(&self) -> Result { + self.count_rows().await + } + + async fn health_check(&self) -> bool { + let Ok(db) = self.client().await else { + return false; + }; + db.query("RETURN 1;").await.is_ok() + } +} + +fn trim_optional(value: Option) -> Option { + value.and_then(|raw| { + let trimmed = raw.trim(); + if trimmed.is_empty() { + None + } else { + Some(trimmed.to_string()) + } + }) +} + +fn non_empty_or_default(value: Option, default_value: &str) -> String { + trim_optional(value).unwrap_or_else(|| default_value.to_string()) +} + +fn parse_endpoint(raw_endpoint: &str) -> Result { + let raw = raw_endpoint.trim(); + if raw.is_empty() { + anyhow::bail!("SurrealDB endpoint URL cannot be empty"); + } + + Url::parse(raw) + .or_else(|_| Url::parse(&format!("http://{raw}"))) + .with_context(|| format!("invalid SurrealDB endpoint URL '{raw_endpoint}'")) +} + +fn is_loopback_host(host: &str) -> bool { + host.eq_ignore_ascii_case("localhost") + || host == "127.0.0.1" + || host == "::1" + || host == "[::1]" +} + +fn validate_endpoint_security(endpoint: &Url, allow_http_loopback: bool) -> Result<()> { + let scheme = endpoint.scheme(); + let host = endpoint.host_str().unwrap_or_default(); + + match scheme { + "https" | "wss" => Ok(()), + "http" | "ws" => { + if !allow_http_loopback { + anyhow::bail!( + "refusing insecure SurrealDB URL: {scheme} is disabled (set \ + memory.surreal.allow_http_loopback=true for localhost only)" + ); + } + if !is_loopback_host(host) { + anyhow::bail!( + "refusing insecure SurrealDB URL over {scheme} for non-loopback host '{host}'" + ); + } + Ok(()) + } + other => anyhow::bail!( + "unsupported SurrealDB URL scheme '{other}', expected one of http/https/ws/wss" + ), + } +} + +fn normalize_ws_endpoint(mut endpoint: Url) -> Result { + match endpoint.scheme() { + "http" => endpoint + .set_scheme("ws") + .map_err(|_| anyhow::anyhow!("failed converting http endpoint to ws"))?, + "https" => endpoint + .set_scheme("wss") + .map_err(|_| anyhow::anyhow!("failed converting https endpoint to wss"))?, + "ws" | "wss" => {} + other => anyhow::bail!( + "unsupported SurrealDB URL scheme '{other}', expected one of http/https/ws/wss" + ), + } + + if endpoint.path().is_empty() || endpoint.path() == "/" { + endpoint.set_path("/rpc"); + } + + Ok(endpoint.to_string()) +} + +fn is_loopback_host_from_endpoint(endpoint: &str) -> bool { + Url::parse(endpoint) + .ok() + .and_then(|url| url.host_str().map(ToOwned::to_owned)) + .is_some_and(|host| is_loopback_host(&host)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::config::{MemoryConfig, SurrealMemoryConfig}; + use crate::memory::embeddings::NoopEmbedding; + use tempfile::TempDir; + + #[test] + fn normalize_http_to_ws_rpc() { + let url = parse_endpoint("http://127.0.0.1:8000").unwrap(); + let normalized = normalize_ws_endpoint(url).unwrap(); + assert_eq!(normalized, "ws://127.0.0.1:8000/rpc"); + } + + #[test] + fn reject_plain_http_non_loopback() { + let endpoint = parse_endpoint("http://10.0.0.7:8000").unwrap(); + let err = validate_endpoint_security(&endpoint, true).unwrap_err(); + assert!(err.to_string().contains("non-loopback")); + } + + #[test] + fn reject_plain_ws_non_loopback() { + let endpoint = parse_endpoint("ws://10.0.0.7:8000/rpc").unwrap(); + let err = validate_endpoint_security(&endpoint, true).unwrap_err(); + assert!(err.to_string().contains("non-loopback")); + } + + #[test] + fn reject_http_when_loopback_override_disabled() { + let endpoint = parse_endpoint("http://127.0.0.1:8000").unwrap(); + let err = validate_endpoint_security(&endpoint, false).unwrap_err(); + assert!(err.to_string().contains("http is disabled")); + } + + #[tokio::test] + async fn backend_smoke_store_and_get_when_test_server_available() { + let Ok(endpoint) = std::env::var("CORVUS_TEST_SURREALDB_URL") else { + return; + }; + + let tmp = TempDir::new().unwrap(); + let mut config = MemoryConfig { + backend: "surreal".to_string(), + ..MemoryConfig::default() + }; + config.surreal = SurrealMemoryConfig { + url: Some(endpoint), + namespace: Some(format!("test_ns_{}", uuid::Uuid::new_v4())), + database: Some(format!("test_db_{}", uuid::Uuid::new_v4())), + username: std::env::var("CORVUS_TEST_SURREALDB_USERNAME").ok(), + password: std::env::var("CORVUS_TEST_SURREALDB_PASSWORD").ok(), + token: std::env::var("CORVUS_TEST_SURREALDB_TOKEN").ok(), + allow_http_loopback: true, + }; + + let memory = + SurrealMemory::new(tmp.path(), &config, Arc::new(NoopEmbedding), 0.7, 0.3).unwrap(); + + memory + .store( + "smoke_key", + "surreal smoke content", + MemoryCategory::Core, + Some("s1"), + ) + .await + .unwrap(); + let got = memory.get("smoke_key").await.unwrap(); + assert!(got.is_some()); + assert_eq!(got.unwrap().key, "smoke_key"); + assert!(memory.count().await.unwrap() >= 1); + } +} diff --git a/clients/agent-runtime/src/onboard/wizard.rs b/clients/agent-runtime/src/onboard/wizard.rs index e5f1fcb93..f9c0cca0c 100755 --- a/clients/agent-runtime/src/onboard/wizard.rs +++ b/clients/agent-runtime/src/onboard/wizard.rs @@ -2,7 +2,7 @@ use crate::config::schema::{DingTalkConfig, IrcConfig, QQConfig, StreamMode, Wha use crate::config::{ AutonomyConfig, BrowserConfig, ChannelsConfig, ComposioConfig, Config, DiscordConfig, HeartbeatConfig, IMessageConfig, MatrixConfig, MemoryConfig, ObservabilityConfig, - RuntimeConfig, SecretsConfig, SlackConfig, TelegramConfig, WebhookConfig, + RuntimeConfig, SecretsConfig, SlackConfig, SurrealMemoryConfig, TelegramConfig, WebhookConfig, }; use crate::hardware::{self, HardwareConfig}; use crate::memory::{ @@ -14,7 +14,8 @@ use crate::providers::{ }; use anyhow::{bail, Context, Result}; use console::style; -use dialoguer::{Confirm, Input, Select}; +use dialoguer::{Confirm, Input, Password, Select}; +use directories::UserDirs; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::BTreeSet; @@ -255,7 +256,8 @@ pub fn run_channels_repair_wizard() -> Result { // ── Quick setup (zero prompts) ─────────────────────────────────── /// Non-interactive setup: generates a sensible default config instantly. -/// Use `corvus onboard` or `corvus onboard --api-key sk-... --provider openrouter --memory sqlite|lucid`. +/// Use `corvus onboard` or +/// `corvus onboard --api-key sk-... --provider openrouter --memory sqlite|lucid|surreal`. /// Use `corvus onboard --interactive` for the full wizard. fn backend_key_from_choice(choice: usize) -> &'static str { selectable_memory_backends() @@ -292,6 +294,7 @@ fn memory_config_defaults_for_backend(backend: &str) -> MemoryConfig { snapshot_on_hygiene: false, auto_hydrate: true, sqlite_open_timeout_secs: None, + surreal: SurrealMemoryConfig::default(), } } @@ -324,6 +327,10 @@ pub fn run_quick_setup( let memory_backend_name = memory_backend .unwrap_or(default_memory_backend_key()) .to_string(); + #[cfg(not(feature = "memory-surreal"))] + if memory_backend_name == "surreal" { + bail!("memory backend 'surreal' requires a binary built with feature 'memory-surreal'"); + } // Create memory config based on backend choice let memory_config = memory_config_defaults_for_backend(&memory_backend_name); @@ -2418,6 +2425,179 @@ fn setup_project_context() -> Result { // ── Step 6: Memory Configuration ─────────────────────────────── +fn scaffold_surreal_docker_files() -> Result { + let home = UserDirs::new() + .map(|dirs| dirs.home_dir().to_path_buf()) + .context("Could not find home directory")?; + let surreal_dir = home.join(".corvus").join("surreal"); + fs::create_dir_all(&surreal_dir).context("Failed to create Surreal helper directory")?; + + let compose_path = surreal_dir.join("docker-compose.yml"); + let env_example_path = surreal_dir.join(".env.example"); + + let compose_contents = r#"services: + surrealdb: + image: surrealdb/surrealdb:v2.3 + # Do not use the in-memory backend ("memory") in production; it is non-persistent. + command: start --log info --user ${SURREALDB_USER:-corvus} --pass ${SURREALDB_PASS:-corvus-pass} surrealkv://data/corvus.db + ports: + - "8000:8000" + volumes: + - surrealdb-data:/data + healthcheck: + test: ["CMD", "surreal", "is-ready", "--conn", "http://127.0.0.1:8000"] + interval: 10s + timeout: 5s + retries: 10 + +volumes: + surrealdb-data: +"#; + + let env_contents = r#"# Copy to .env and adjust values. +SURREALDB_USER=corvus +SURREALDB_PASS=corvus-pass + +# Corvus env-first memory config +CORVUS_SURREALDB_URL=http://127.0.0.1:8000 +CORVUS_SURREALDB_NAMESPACE=corvus +CORVUS_SURREALDB_DATABASE=memory +CORVUS_SURREALDB_USERNAME=corvus +CORVUS_SURREALDB_PASSWORD=corvus-pass +"#; + + fs::write(&compose_path, compose_contents).context("Failed to write Surreal docker compose")?; + fs::write(&env_example_path, env_contents).context("Failed to write Surreal env example")?; + + Ok(surreal_dir) +} + +#[allow(clippy::too_many_lines)] +fn setup_surreal_memory_options(config: &mut MemoryConfig) -> Result<()> { + print_bullet("SurrealDB backend selected."); + print_bullet("Environment variables are preferred for credentials (recommended)."); + print_bullet("If you save credentials here, they are encrypted when [secrets].encrypt=true."); + println!(); + + println!(" Expected env vars (env-first):"); + println!(" - CORVUS_SURREALDB_URL"); + println!(" - CORVUS_SURREALDB_NAMESPACE"); + println!(" - CORVUS_SURREALDB_DATABASE"); + println!(" - CORVUS_SURREALDB_USERNAME"); + println!(" - CORVUS_SURREALDB_PASSWORD"); + println!(" - CORVUS_SURREALDB_TOKEN"); + println!(); + + let endpoint: String = Input::new() + .with_prompt(" SurrealDB URL") + .default( + config + .surreal + .url + .clone() + .unwrap_or_else(|| "http://127.0.0.1:8000".to_string()), + ) + .interact_text()?; + let namespace: String = Input::new() + .with_prompt(" Namespace") + .default( + config + .surreal + .namespace + .clone() + .unwrap_or_else(|| "corvus".to_string()), + ) + .interact_text()?; + let database: String = Input::new() + .with_prompt(" Database") + .default( + config + .surreal + .database + .clone() + .unwrap_or_else(|| "memory".to_string()), + ) + .interact_text()?; + + let auth_modes = vec![ + "Use environment variables only (recommended)", + "Save username/password fallback now", + "Save token fallback now", + ]; + let auth_choice = Select::new() + .with_prompt(" Authentication fallback") + .items(&auth_modes) + .default(0) + .interact()?; + + let (username, password, token) = match auth_choice { + 1 => { + let user: String = Input::new() + .with_prompt(" SurrealDB username") + .allow_empty(true) + .interact_text()?; + let pass = Password::new() + .with_prompt(" SurrealDB password (hidden)") + .allow_empty_password(true) + .interact()?; + (trim_non_empty(user), trim_non_empty(pass), None) + } + 2 => { + let token = Password::new() + .with_prompt(" SurrealDB token (hidden)") + .allow_empty_password(true) + .interact()?; + (None, None, trim_non_empty(token)) + } + _ => (None, None, None), + }; + + config.surreal = SurrealMemoryConfig { + url: trim_non_empty(endpoint), + namespace: trim_non_empty(namespace), + database: trim_non_empty(database), + username, + password, + token, + allow_http_loopback: true, + }; + + let generate_docker = Confirm::new() + .with_prompt(" Generate local SurrealDB Docker helper files in ~/.corvus/surreal?") + .default(true) + .interact()?; + if generate_docker { + let helper_dir = scaffold_surreal_docker_files()?; + println!( + " {} Docker helper files generated:", + style("✓").green().bold(), + ); + println!( + " {}", + style(helper_dir.join("docker-compose.yml").display()).green() + ); + println!( + " {}", + style(helper_dir.join(".env.example").display()).green() + ); + println!(" Run manually:"); + println!(" cd {}", style(helper_dir.display()).green()); + println!(" cp .env.example .env"); + println!(" docker compose up -d"); + } + + Ok(()) +} + +fn trim_non_empty(value: String) -> Option { + let trimmed = value.trim(); + if trimmed.is_empty() { + None + } else { + Some(trimmed.to_string()) + } +} + fn setup_memory() -> Result { print_bullet("Choose how Corvus stores and searches memories."); print_bullet("You can always change this later in config.toml."); @@ -2452,6 +2632,11 @@ fn setup_memory() -> Result { let mut config = memory_config_defaults_for_backend(backend); config.auto_save = auto_save; + + if backend == "surreal" { + setup_surreal_memory_options(&mut config)?; + } + Ok(config) } @@ -4105,10 +4290,8 @@ fn print_summary(config: &Config) { ); println!( " {}", - style( - "or: corvus auth paste-token --provider anthropic --auth-kind authorization" - ) - .yellow() + style("or: corvus auth paste-token --provider anthropic --auth-kind authorization") + .yellow() ); } else { let env_var = provider_env_var(provider); @@ -4870,8 +5053,17 @@ mod tests { fn backend_key_from_choice_maps_supported_backends() { assert_eq!(backend_key_from_choice(0), "sqlite"); assert_eq!(backend_key_from_choice(1), "lucid"); - assert_eq!(backend_key_from_choice(2), "markdown"); - assert_eq!(backend_key_from_choice(3), "none"); + #[cfg(feature = "memory-surreal")] + { + assert_eq!(backend_key_from_choice(2), "surreal"); + assert_eq!(backend_key_from_choice(3), "markdown"); + assert_eq!(backend_key_from_choice(4), "none"); + } + #[cfg(not(feature = "memory-surreal"))] + { + assert_eq!(backend_key_from_choice(2), "markdown"); + assert_eq!(backend_key_from_choice(3), "none"); + } assert_eq!(backend_key_from_choice(999), "sqlite"); } @@ -4917,4 +5109,18 @@ mod tests { assert_eq!(config.purge_after_days, 0); assert_eq!(config.embedding_cache_size, 0); } + + #[test] + fn memory_config_defaults_include_surreal_defaults() { + let config = memory_config_defaults_for_backend("sqlite"); + assert_eq!(config.surreal.namespace.as_deref(), Some("corvus")); + assert_eq!(config.surreal.database.as_deref(), Some("memory")); + assert!(config.surreal.allow_http_loopback); + } + + #[test] + fn trim_non_empty_returns_none_for_blank() { + assert_eq!(trim_non_empty(" ".into()), None); + assert_eq!(trim_non_empty("value".into()), Some("value".into())); + } } diff --git a/clients/agent-runtime/tests/memory_comparison.rs b/clients/agent-runtime/tests/memory_comparison.rs index d7e2be6d2..7b1536f5a 100755 --- a/clients/agent-runtime/tests/memory_comparison.rs +++ b/clients/agent-runtime/tests/memory_comparison.rs @@ -7,6 +7,8 @@ use tempfile::TempDir; // We test both backends through the public memory module use corvus::memory::{markdown::MarkdownMemory, sqlite::SqliteMemory, Memory, MemoryCategory}; +#[cfg(feature = "memory-surreal")] +use corvus::{config::MemoryConfig, memory::surreal::SurrealMemory}; // ── Helpers ──────────────────────────────────────────────────── @@ -18,6 +20,27 @@ fn markdown_backend(dir: &std::path::Path) -> MarkdownMemory { MarkdownMemory::new(dir) } +#[cfg(feature = "memory-surreal")] +fn maybe_surreal_backend(dir: &std::path::Path) -> Option { + let endpoint = std::env::var("CORVUS_TEST_SURREALDB_URL").ok()?; + let mut config = MemoryConfig { + backend: "surreal".to_string(), + ..MemoryConfig::default() + }; + config.surreal.url = Some(endpoint); + config.surreal.namespace = Some(format!("memory_cmp_ns_{}", uuid::Uuid::new_v4())); + config.surreal.database = Some(format!("memory_cmp_db_{}", uuid::Uuid::new_v4())); + config.surreal.username = std::env::var("CORVUS_TEST_SURREALDB_USERNAME").ok(); + config.surreal.password = std::env::var("CORVUS_TEST_SURREALDB_PASSWORD").ok(); + config.surreal.token = std::env::var("CORVUS_TEST_SURREALDB_TOKEN").ok(); + config.surreal.allow_http_loopback = true; + + let embedder: std::sync::Arc = + std::sync::Arc::new(corvus::memory::embeddings::NoopEmbedding); + + SurrealMemory::new(dir, &config, embedder, 0.7, 0.3).ok() +} + // ── Test 1: Store performance ────────────────────────────────── #[tokio::test] @@ -445,3 +468,36 @@ async fn compare_category_filter() { assert!(!md_core.is_empty()); assert!(!md_all.is_empty()); } + +#[cfg(feature = "memory-surreal")] +#[tokio::test] +async fn compare_surreal_store_and_recall_when_available() { + let tmp = TempDir::new().unwrap(); + let Some(sr) = maybe_surreal_backend(tmp.path()) else { + return; + }; + + sr.store( + "lang_pref", + "User prefers Rust over Python", + MemoryCategory::Core, + Some("session-surreal"), + ) + .await + .unwrap(); + sr.store( + "deploy", + "Deploys with Docker Compose", + MemoryCategory::Daily, + Some("session-surreal"), + ) + .await + .unwrap(); + + let got = sr.get("lang_pref").await.unwrap(); + assert!(got.is_some()); + + let recall = sr.recall("Rust", 5, Some("session-surreal")).await.unwrap(); + assert!(!recall.is_empty()); + assert!(recall.iter().any(|item| item.key == "lang_pref")); +} diff --git a/dev/config.template.toml b/dev/config.template.toml index acb9e58da..ee1f00aa9 100755 --- a/dev/config.template.toml +++ b/dev/config.template.toml @@ -6,6 +6,18 @@ default_provider = "ollama" default_model = "llama3.2" default_temperature = 0.7 +[memory] +backend = "sqlite" # default_memory_backend_key() default; set to "surreal" to use the block below +auto_save = true + +[memory.surreal] +url = "http://127.0.0.1:8000" +namespace = "corvus" +database = "memory" +username = "corvus" +password = "corvus-pass" +allow_http_loopback = true + [gateway] port = 3000 host = "127.0.0.1"