From 37ce849dd5d92da13264db82d64016ff827e815d Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Wed, 17 Jun 2026 12:32:23 +1000 Subject: [PATCH 1/4] Add stackql-agent demo + self-healing bundle cache; talk kit stackql-agent: a Rust-native agent over the cloud estate, for the meetup talk. Embeds stackql-mcp and wires the 13 StackQL MCP tools into a rig 0.38 agent (rmcp 1.7, claude-opus-4-8). One binary, three personas (platform engineering, SRE, audit) that differ only by system prompt; the embedded backend and read-only contract are identical. Streaming REPL prints each tool call live; one-shot (-p) and --check pre-flight modes included. Default github null_auth runs with zero credentials; --auth points the same agent at credentialed providers (IGA, CSPM, FinOps, AWS/Google/Azure). Vendored single-binary build verified (~123 MB, no runtime downloads). The integration seam is ~10 lines: server.list_all_tools() + server.peer() feed straight into rig's rmcp_tools(). Verified at runtime via --check (server starts, self-heals the cache, exposes 13 tools); the LLM loop needs the caller's ANTHROPIC_API_KEY. bundle: make extraction self-healing. A non-empty but invalid cache dir (left by an interrupted run, or a stale layout) previously caused the atomic rename to fail with ENOTEMPTY and bricked the binary until the cache was cleared by hand. Now such a dir is replaced (or adopted if a concurrent extractor produced a valid one). Regression test added. This surfaced live while building the agent and is a fix for the published crate (0.1.1 material). talk.md: presentation kit - slide-by-slide content (intro StackQL, the MCP server, agentic use cases, embedded/vendored primitives in Rust, the demo, the ask) with speaker notes, plus a demo runbook with exact per-persona commands, the live-vs-credentialed framing, and a fallback plan for wifi/rate-limit/API failure. Co-Authored-By: Claude Opus 4.8 (1M context) --- Cargo.lock | 1394 +++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- README.md | 12 + src/bundle.rs | 36 +- stackql-agent/Cargo.toml | 25 + stackql-agent/README.md | 80 ++ stackql-agent/src/main.rs | 231 ++++++ stackql-agent/src/persona.rs | 117 +++ talk.md | 289 +++++++ 9 files changed, 2165 insertions(+), 21 deletions(-) create mode 100644 stackql-agent/Cargo.toml create mode 100644 stackql-agent/README.md create mode 100644 stackql-agent/src/main.rs create mode 100644 stackql-agent/src/persona.rs create mode 100644 talk.md diff --git a/Cargo.lock b/Cargo.lock index 4ed2c5b..d400b03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,40 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-any" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -90,6 +124,12 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "auditron" version = "0.1.0" @@ -117,6 +157,28 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "aws-lc-rs" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "base64" version = "0.22.1" @@ -172,6 +234,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -181,6 +245,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.45" @@ -212,7 +282,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", ] [[package]] @@ -221,7 +291,7 @@ version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn", @@ -233,12 +303,31 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "compact_str" version = "0.8.2" @@ -253,6 +342,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.18.1" @@ -282,6 +380,26 @@ dependencies = [ "url", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -381,7 +499,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn", ] @@ -396,6 +514,53 @@ dependencies = [ "syn", ] +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "deluxe" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed332aaf752b459088acf3dd4eca323e3ef4b83c70a84ca48fb0ec5305f1488" +dependencies = [ + "deluxe-core", + "deluxe-macros", + "once_cell", + "proc-macro2", + "syn", +] + +[[package]] +name = "deluxe-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddada51c8576df9d6a8450c351ff63042b092c9458b8ac7d20f89cbd0ffd313" +dependencies = [ + "arrayvec", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "deluxe-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87546d9c837f0b7557e47b8bd6eae52c3c223141b76aa233c345c9ab41d9117" +dependencies = [ + "deluxe-core", + "heck 0.4.1", + "if_chain", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "deranged" version = "0.5.8" @@ -435,12 +600,33 @@ dependencies = [ "litrs", ] +[[package]] +name = "dunce" +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 = "either" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -457,6 +643,23 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -474,6 +677,12 @@ dependencies = [ "zlib-rs", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.1.5" @@ -489,6 +698,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.32" @@ -560,6 +775,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +[[package]] +name = "futures-timer" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" + [[package]] name = "futures-util" version = "0.3.32" @@ -594,8 +815,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] @@ -615,6 +877,12 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -631,12 +899,96 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -770,6 +1122,12 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "if_chain" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb" + [[package]] name = "indexmap" version = "2.14.0" @@ -802,6 +1160,12 @@ dependencies = [ "syn", ] +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -824,30 +1188,89 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] -name = "js-sys" -version = "0.3.102" +name = "jni" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" dependencies = [ "cfg-if", - "futures-util", - "wasm-bindgen", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror", + "walkdir", + "windows-link", ] [[package]] -name = "libc" -version = "0.2.186" +name = "jni-macros" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", +] [[package]] -name = "linux-raw-sys" -version = "0.4.15" +name = "jni-sys" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] [[package]] -name = "litemap" +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" @@ -882,12 +1305,40 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "memchr" version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -910,6 +1361,25 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "nanoid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" +dependencies = [ + "rand 0.8.6", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-conv" version = "0.2.2" @@ -937,6 +1407,21 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -966,12 +1451,38 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -993,6 +1504,34 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.12+spec-1.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -1002,6 +1541,62 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.45" @@ -1011,6 +1606,71 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "ratatui" version = "0.29.0" @@ -1041,6 +1701,121 @@ dependencies = [ "bitflags", ] +[[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", +] + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rig-core" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557f11c26c2c2ea61d9cb843ce5adff138cc5785f58ff4b87d779de8acaa32ae" +dependencies = [ + "as-any", + "async-stream", + "base64", + "bytes", + "eventsource-stream", + "fastrand", + "futures", + "futures-timer", + "glob", + "http", + "mime", + "mime_guess", + "nanoid", + "ordered-float", + "pin-project-lite", + "reqwest", + "rig-derive", + "rmcp", + "schemars", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-futures", + "url", +] + +[[package]] +name = "rig-derive" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643eb495acbd4bd5976164cd068f186d534f741def8d5d052f860266b98d07f8" +dependencies = [ + "convert_case", + "deluxe", + "indoc", + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + [[package]] name = "ring" version = "0.17.14" @@ -1049,7 +1824,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -1062,9 +1837,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0810a9f717d9828f475fe1f629f4c305c8464b7f496c3a854b58d29e65f4058e" dependencies = [ "async-trait", + "base64", "chrono", "futures", + "pastey", "pin-project-lite", + "rmcp-macros", + "schemars", "serde", "serde_json", "thiserror", @@ -1074,6 +1853,34 @@ dependencies = [ "tracing", ] +[[package]] +name = "rmcp-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aefac48c364756e97f04c0401ba3231e8607882c7c1d92da0437dc16307904d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[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" @@ -1093,6 +1900,7 @@ version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -1102,21 +1910,62 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ + "web-time", "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -1134,12 +1983,85 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "chrono", + "dyn-clone", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.228" @@ -1170,6 +2092,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_json" version = "1.0.150" @@ -1196,6 +2129,17 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.9" @@ -1248,7 +2192,23 @@ dependencies = [ name = "simd-adler32" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "slab" @@ -1262,12 +2222,36 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "stackql-agent" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "futures", + "rig-core", + "rmcp", + "serde_json", + "stackql-mcp", + "tokio", +] + [[package]] name = "stackql-mcp" version = "0.1.0" @@ -1287,6 +2271,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" @@ -1308,7 +2298,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", @@ -1332,6 +2322,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -1343,6 +2342,27 @@ dependencies = [ "syn", ] +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "thiserror" version = "2.0.18" @@ -1404,6 +2424,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.52.3" @@ -1415,6 +2450,7 @@ dependencies = [ "mio", "pin-project-lite", "signal-hook-registry", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -1430,6 +2466,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.18" @@ -1441,6 +2487,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots 0.26.11", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -1454,6 +2516,98 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.44" @@ -1485,6 +2639,43 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "futures", + "futures-task", + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "typed-path" version = "0.12.3" @@ -1497,6 +2688,12 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1561,7 +2758,7 @@ dependencies = [ "serde_json", "ureq-proto", "utf8-zero", - "webpki-roots", + "webpki-roots 1.0.7", ] [[package]] @@ -1588,6 +2785,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8-zero" version = "0.8.1" @@ -1612,12 +2815,40 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.125" @@ -1631,6 +2862,16 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.125" @@ -1663,6 +2904,57 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.7", +] + [[package]] name = "webpki-roots" version = "1.0.7" @@ -1688,6 +2980,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1735,6 +3036,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -1835,6 +3147,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "writeable" version = "0.6.3" @@ -1864,6 +3200,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zerofrom" version = "0.1.8" diff --git a/Cargo.toml b/Cargo.toml index a4ddd94..fe45c40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["auditron"] +members = ["auditron", "stackql-agent"] resolver = "3" [package] diff --git a/README.md b/README.md index 99938fa..09d8461 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,18 @@ STACKQL_MCP_BUNDLE_FILE=$BUNDLE cargo build -p auditron --features vendored --re The resulting binary (~80 MB) carries the StackQL server inside and runs on a clean machine with no downloads. +## Demo app: stackql-agent + +[stackql-agent](stackql-agent) is the agentic companion: it embeds this crate and wires the StackQL MCP tools into a [rig](https://docs.rig.rs) agent. One binary becomes a platform-engineering, SRE, or audit agent by swapping a system prompt - the backend and the read-only contract are identical across all three. + +```sh +export ANTHROPIC_API_KEY=sk-ant-... +cargo run -p stackql-agent -- --persona platform # also: sre, audit +cargo run -p stackql-agent -- --check # pre-flight, no model calls +``` + +It runs against public GitHub data with zero cloud credentials; point `--auth` at a credentialed provider for IGA, CSPM, FinOps, and AWS/Google/Azure. The integration is about ten lines: `server.list_all_tools()` plus `server.peer()` feed straight into rig's `rmcp_tools()`. + ## Development ```sh diff --git a/src/bundle.rs b/src/bundle.rs index 6245648..c2a00a7 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -57,10 +57,23 @@ pub fn extract_bundle(reader: R, dest: &Path) -> Result } let result = extract_into(reader, &tmp).and_then(|entry| { + // We only reach here when `dest` was not a valid cache at entry. If + // something already occupies `dest`, it is either a valid cache a + // concurrent extractor just produced (use it) or a stale/partial dir + // from an interrupted run (replace it). Without this, `rename` fails + // with ENOTEMPTY on a non-empty `dest` and the binary is bricked until + // the cache is cleared by hand. + if dest.exists() { + if let Some(binary) = cached_binary(dest) { + let _ = fs::remove_dir_all(&tmp); + return Ok(binary); + } + fs::remove_dir_all(dest)?; + } match fs::rename(&tmp, dest) { Ok(()) => {} Err(_) if cached_binary(dest).is_some() => { - // Another process won the race; its copy is valid. + // A concurrent extractor won the race; its copy is valid. let _ = fs::remove_dir_all(&tmp); } Err(e) => return Err(Error::Io(e)), @@ -158,6 +171,27 @@ mod tests { fs::remove_dir_all(dest.parent().unwrap()).unwrap(); } + #[test] + fn replaces_a_stale_invalid_cache_dir() { + // A non-empty `dest` that is not a valid cached bundle (e.g. left by an + // interrupted extraction) must be replaced, not cause ENOTEMPTY. + let dest = temp_dest("stale"); + fs::create_dir_all(dest.join("server")).unwrap(); + fs::write(dest.join("partial.tmp"), b"junk").unwrap(); + assert!( + cached_binary(&dest).is_none(), + "precondition: dest is invalid" + ); + + let binary = extract_bundle(Cursor::new(fake_bundle("server/stackql")), &dest).unwrap(); + assert!(binary.is_file()); + assert!( + !dest.join("partial.tmp").exists(), + "stale contents should be gone" + ); + fs::remove_dir_all(dest.parent().unwrap()).unwrap(); + } + #[test] fn rejects_traversal_in_entry_point() { let dest = temp_dest("traversal"); diff --git a/stackql-agent/Cargo.toml b/stackql-agent/Cargo.toml new file mode 100644 index 0000000..88cbd5f --- /dev/null +++ b/stackql-agent/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "stackql-agent" +version = "0.1.0" +edition = "2021" +rust-version = "1.88" +publish = false +description = "Rust-native agent over your cloud estate: an embedded StackQL MCP server wired into a rig agent. One backend, three personas (platform engineering, SRE, audit)." +license = "MIT" + +[features] +default = [] +# Single-binary build: embed the .mcpb at compile time via +# STACKQL_MCP_BUNDLE_FILE (see stackql_mcp::fetch_bundle). +vendored = ["stackql-mcp/vendored"] + +[dependencies] +anyhow = "1" +clap = { version = "4", features = ["derive"] } +futures = "0.3" +# rig-core's lib is `rig_core`; rename to `rig` to match rig's own docs/examples. +rig = { package = "rig-core", version = "0.38", features = ["rmcp"] } +rmcp = { version = "1.7", default-features = false, features = ["client"] } +serde_json = "1" +stackql-mcp = { path = ".." } +tokio = { version = "1", features = ["macros", "rt-multi-thread", "io-std", "io-util"] } diff --git a/stackql-agent/README.md b/stackql-agent/README.md new file mode 100644 index 0000000..5946b65 --- /dev/null +++ b/stackql-agent/README.md @@ -0,0 +1,80 @@ +# stackql-agent + +A Rust-native agent over your cloud and SaaS estate. It embeds the StackQL MCP server (via [stackql-mcp](https://crates.io/crates/stackql-mcp)) and wires it into a [rig](https://docs.rig.rs) agent. The agent's tools are the StackQL MCP tools; the persona is just a system prompt. + +One binary becomes a platform-engineering, SRE, or audit agent by swapping `--persona`. The backend, the tools, and the read-only safety contract are identical across all three. This is the demo companion to [auditron](../auditron) (the deterministic, evidence-pack sibling) and a worked example of embedding and vendoring an MCP server into a Rust agent. + +## Quickstart + +Needs `ANTHROPIC_API_KEY`. The default GitHub provider runs in `null_auth` mode, so it queries public data with zero cloud credentials. + +```sh +export ANTHROPIC_API_KEY=sk-ant-... + +# Interactive REPL, platform-engineering persona +cargo run -p stackql-agent -- --persona platform + +# One-shot +cargo run -p stackql-agent -- --persona sre \ + -p "Show the most recent failed workflow runs for stackql/stackql" + +# Pre-flight: start the server, list the MCP tools, exit. No model calls. +cargo run -p stackql-agent -- --check +``` + +## Personas + +The only difference between them is the system prompt in [src/persona.rs](src/persona.rs). + +| Persona | Focus | +|---|---| +| `platform` | Platform engineering: repo and CI configuration, branch protection, golden-path conformance, drift | +| `sre` | Site reliability: workflow runs, failures and blast radius, what changed, triage | +| `audit` | Compliance: IGA and entitlements, security posture (CSPM), and cost (FinOps), with verifiable findings | + +## Options + +```text +--persona Which system prompt to wear (default: platform) +--provider Provider to pull and query (default: github) +--auth Provider auth document (default: github null_auth) +--prompt, -p One-shot: answer and exit +--max-turns Max agent tool-call rounds per question (default: 20) +--check Pre-flight: start server, list tools, exit (no model calls) +``` + +To point at a credentialed provider, pass its auth document, for example: + +```sh +cargo run -p stackql-agent -- --persona audit --provider aws \ + --auth '{"aws":{"type":"aws_signing_v4"}}' \ + -p "Which security groups allow 0.0.0.0/0 on port 22?" +``` + +## Single-binary build + +Embed the StackQL server in the binary so it runs with no runtime downloads: + +```sh +BUNDLE=$(cargo run -q -p stackql-mcp --example fetch_bundle) +STACKQL_MCP_BUNDLE_FILE=$BUNDLE cargo build -p stackql-agent --features vendored --release +``` + +The result is a self-contained binary that carries the StackQL engine inside it. + +## How the wiring works + +```rust +let server = StackqlMcp::builder().mode(Mode::ReadOnly).auth(auth).start().await?; +let tools = server.list_all_tools().await?; // the StackQL MCP tools +let sink = server.peer().to_owned(); // the rmcp client peer + +let agent = anthropic::Client::from_env()? + .agent(anthropic::completion::CLAUDE_OPUS_4_8) + .preamble(&persona.preamble) + .rmcp_tools(tools, sink) // rig consumes rmcp directly + .default_max_turns(20) + .build(); +``` + +`stackql-mcp` hands back a connected rmcp client; rig 0.38's `rmcp_tools()` consumes it as-is (both are on rmcp 1.7). See [src/main.rs](src/main.rs) for the streaming REPL that prints each tool call as the agent makes it. diff --git a/stackql-agent/src/main.rs b/stackql-agent/src/main.rs new file mode 100644 index 0000000..bb19845 --- /dev/null +++ b/stackql-agent/src/main.rs @@ -0,0 +1,231 @@ +//! stackql-agent - a Rust-native agent over your cloud estate. +//! +//! An embedded StackQL MCP server (vendored straight into this binary, or +//! downloaded as a verified sidecar) wired into a `rig` agent. The agent's +//! tools ARE the StackQL MCP tools; the persona is just a system prompt. Swap +//! the persona and the same backend becomes a platform-engineering, SRE, or +//! audit agent. +//! +//! Needs `ANTHROPIC_API_KEY`. Diagnostics and tool-call traces go to stderr; +//! the agent's answer goes to stdout. + +mod persona; + +use std::io::Write as _; + +use anyhow::{Context, Result}; +use clap::Parser; +use futures::StreamExt; +use rig::agent::{Agent, MultiTurnStreamItem}; +use rig::client::{CompletionClient, ProviderClient}; +use rig::providers::anthropic; +use rig::providers::anthropic::completion::CompletionModel; +use rig::streaming::{StreamedAssistantContent, StreamingPrompt}; +use rmcp::model::CallToolRequestParams; +use stackql_mcp::{Mode, StackqlMcp}; +use tokio::io::AsyncBufReadExt as _; + +#[derive(Parser)] +#[command( + name = "stackql-agent", + version, + about = "Rust-native agent over your cloud estate, on an embedded StackQL MCP server" +)] +struct Cli { + /// Which persona to wear: platform | sre | audit. Only the system prompt + /// changes between them. + #[arg(long, default_value = "platform")] + persona: String, + /// Provider to pull and query (github runs with zero credentials). + #[arg(long, default_value = "github")] + provider: String, + /// Provider auth document. Defaults to github null_auth (no credentials). + /// Pass a JSON document to use a credentialed provider, e.g. + /// --auth '{"aws":{"type":"aws_signing_v4"}}'. + #[arg(long)] + auth: Option, + /// One-shot: answer this prompt and exit, instead of the REPL. + #[arg(long, short)] + prompt: Option, + /// Max agent turns (tool-call rounds) per question. + #[arg(long, default_value_t = 20)] + max_turns: usize, + /// Pre-flight: start the server, pull the provider, list the MCP tools the + /// agent will get, and exit. Useful to verify a machine before a demo. No + /// model calls, so no ANTHROPIC_API_KEY required. + #[arg(long)] + check: bool, +} + +#[tokio::main] +async fn main() -> Result<()> { + let cli = Cli::parse(); + + let persona = persona::resolve(&cli.persona).with_context(|| { + format!( + "unknown persona {:?} (choose one of: {})", + cli.persona, + persona::KEYS.join(", ") + ) + })?; + + let auth: serde_json::Value = match &cli.auth { + Some(raw) => serde_json::from_str(raw).context("parsing --auth as JSON")?, + None => serde_json::json!({ &cli.provider: { "type": "null_auth" } }), + }; + + // Start the embedded StackQL MCP server. Vendored when built with the + // feature, otherwise downloaded and sha256-verified on first run. + eprintln!("stackql-agent: starting embedded StackQL MCP server..."); + let builder = StackqlMcp::builder().mode(Mode::ReadOnly).auth(auth); + #[cfg(feature = "vendored")] + let builder = builder.bundle_bytes(stackql_mcp::include_bundle!()); + let server = builder + .start() + .await + .context("starting the embedded stackql mcp server")?; + + // Pull the provider once so the first user query is instant. + pull_provider(&server, &cli.provider).await?; + + // Hand the embedded server's MCP tools to a rig agent. This is the whole + // integration: list_all_tools() + peer() come straight off our connected + // rmcp client, and rig 0.38's rmcp_tools() consumes them directly. + let tools = server.list_all_tools().await.context("listing MCP tools")?; + eprintln!( + "stackql-agent: {} | provider '{}' | {} tools | read_only", + persona.title, + cli.provider, + tools.len() + ); + // Pre-flight: prove the embedded server + MCP wiring works on this machine + // without making any model calls. + if cli.check { + println!("MCP tools available to the agent:"); + for tool in &tools { + println!(" - {}", tool.name); + } + match anthropic::Client::from_env() { + Ok(client) => { + let _agent = client + .agent(anthropic::completion::CLAUDE_OPUS_4_8) + .preamble(&persona.preamble) + .rmcp_tools(tools, server.peer().to_owned()) + .default_max_turns(cli.max_turns) + .build(); + eprintln!("stackql-agent: check OK - agent built (ANTHROPIC_API_KEY present)"); + } + Err(_) => { + eprintln!( + "stackql-agent: check OK - server + {} MCP tools ready (set ANTHROPIC_API_KEY to run the agent)", + tools.len() + ); + } + } + server.shutdown().await.ok(); + return Ok(()); + } + + let sink = server.peer().to_owned(); + + let agent = anthropic::Client::from_env() + .context("ANTHROPIC_API_KEY not set")? + .agent(anthropic::completion::CLAUDE_OPUS_4_8) + .preamble(&persona.preamble) + .rmcp_tools(tools, sink) + .default_max_turns(cli.max_turns) + .build(); + + if let Some(prompt) = cli.prompt { + answer(&agent, &prompt, cli.max_turns).await?; + } else { + repl(&agent, &persona, cli.max_turns).await?; + } + + server.shutdown().await.ok(); + Ok(()) +} + +/// Pull a provider into the server's local cache via the MCP tool. Read-only +/// safe (local cache only) and idempotent. +async fn pull_provider(server: &stackql_mcp::RunningServer, provider: &str) -> Result<()> { + let mut params = CallToolRequestParams::new("pull_provider"); + params.arguments = serde_json::json!({ "provider": provider }) + .as_object() + .cloned(); + server + .call_tool(params) + .await + .with_context(|| format!("pulling provider {provider}"))?; + Ok(()) +} + +async fn repl( + agent: &Agent, + persona: &persona::Persona, + max_turns: usize, +) -> Result<()> { + println!("\nstackql-agent - {}", persona.title); + println!("Ask about your estate in plain English. Try:"); + for example in persona.examples { + println!(" - {example}"); + } + println!("Type 'quit' or Ctrl-D to exit.\n"); + + let mut reader = tokio::io::BufReader::new(tokio::io::stdin()).lines(); + loop { + print!("you> "); + std::io::stdout().flush().ok(); + let Some(line) = reader.next_line().await? else { + break; + }; + let line = line.trim(); + if line.is_empty() { + continue; + } + if matches!(line, "quit" | "exit" | ":q") { + break; + } + if let Err(e) = answer(agent, line, max_turns).await { + eprintln!("\nstackql-agent: {e:#}"); + } + } + Ok(()) +} + +/// Stream one answer. Text deltas go to stdout as they arrive; each tool the +/// agent invokes is announced on stderr so the audience sees it querying the +/// live estate. +async fn answer(agent: &Agent, prompt: &str, max_turns: usize) -> Result<()> { + let mut stream = agent.stream_prompt(prompt).multi_turn(max_turns).await; + while let Some(item) = stream.next().await { + match item? { + MultiTurnStreamItem::StreamAssistantItem(StreamedAssistantContent::Text(text)) => { + print!("{}", text.text); + std::io::stdout().flush().ok(); + } + MultiTurnStreamItem::StreamAssistantItem(StreamedAssistantContent::ToolCall { + tool_call, + .. + }) => { + eprintln!( + "\n \u{2192} {}({})", + tool_call.function.name, + truncate(&tool_call.function.arguments.to_string(), 200) + ); + } + _ => {} + } + } + println!(); + Ok(()) +} + +fn truncate(s: &str, max: usize) -> String { + if s.chars().count() <= max { + return s.to_string(); + } + let mut out: String = s.chars().take(max).collect(); + out.push_str(" ..."); + out +} diff --git a/stackql-agent/src/persona.rs b/stackql-agent/src/persona.rs new file mode 100644 index 0000000..793efe5 --- /dev/null +++ b/stackql-agent/src/persona.rs @@ -0,0 +1,117 @@ +//! The three agent personas. The only thing that changes between a platform +//! engineering agent and an SRE agent is this system prompt - the embedded +//! StackQL MCP backend and the wiring are identical. That is the whole point. + +/// Shared StackQL grounding spliced into every persona. Encodes the provider +/// quirks that otherwise trip an agent up on live data, so queries land on the +/// first try during a demo. +const STACKQL_GUIDANCE: &str = r#" +You query real cloud and SaaS estates through StackQL, which exposes providers +as SQL. You have MCP tools for this: +- run_select_query(sql): run a read-only SELECT and get rows back +- list_providers / list_services / list_resources / describe_resource: discover + the schema before querying something unfamiliar + +How to write StackQL well: +- Tables are provider.service.resource, e.g. github.repos.repos, + github.repos.branches, github.actions.workflow_runs, github.orgs.members. +- Many resources require key columns in the WHERE clause that map to API path + parameters. Examples: github.repos.repos needs `org = '...'`; + github.repos.branches and github.actions.workflow_runs need + `owner = '...' AND repo = '...'`; github.orgs.members needs `org = '...'`. + If a query errors, describe_resource to find the required columns. +- All result values come back as strings. Booleans render as 'true'/'false' + but their storage varies per resource: compare with `= 0` / `= 1`, never the + strings 'true'/'false' (e.g. `archived = 0`, `protected = 0`). +- Avoid OR and NOT in WHERE clauses; prefer coalesce(col,'') and `= 0`. +- SQLite scalar/date functions work: coalesce(), datetime('now','-12 months'). +- Everything is read-only. Never attempt mutations. + +Be concrete: when you make a claim, show the SQL you ran and the key rows +behind it. Lead with the answer, then the evidence. If a result set is empty, +say so plainly rather than inventing findings - and remember an empty set can +also mean the query hit a provider error, so sanity-check with a broader query +if a "clean" result looks surprising. +"#; + +pub struct Persona { + pub title: &'static str, + pub preamble: String, + pub examples: &'static [&'static str], +} + +pub fn resolve(key: &str) -> Option { + match key { + "platform" => Some(platform()), + "sre" => Some(sre()), + "audit" => Some(audit()), + _ => None, + } +} + +pub const KEYS: &[&str] = &["platform", "sre", "audit"]; + +fn platform() -> Persona { + Persona { + title: "Agentic Platform Engineering", + preamble: format!( + "You are a platform engineering agent. You help platform teams keep \ + their estate consistent and well-governed: repository configuration, \ + branch protection and rulesets, CI/CD setup, required reviews, and \ + standardisation across many repositories. You answer questions about \ + how the estate is configured and where it drifts from good practice, \ + and you propose concrete remediation (the exact setting, the gh/CLI \ + command, or the IaC change) - but you never make changes yourself.\n{STACKQL_GUIDANCE}" + ), + examples: &[ + "Which repos in the stackql org are missing branch protection on their default branch?", + "List the stackql org's repos that have no description or no license, grouped by what's missing.", + "Across the stackql org, which default branches are not called 'main'?", + "Which repos were last updated over a year ago and should probably be archived?", + ], + } +} + +fn sre() -> Persona { + Persona { + title: "Agentic SRE", + preamble: format!( + "You are a site reliability engineering agent. You help SRE and on-call \ + teams understand operational health from live signals: CI/CD workflow \ + runs, failures and their blast radius, what changed recently, and where \ + reliability is trending the wrong way. You triage: surface the failing \ + thing, say which branch and workflow it is, and suggest the next \ + diagnostic step. You investigate and report; you do not take \ + remediating action yourself.\n{STACKQL_GUIDANCE}" + ), + examples: &[ + "Show the most recent failed GitHub Actions workflow runs for stackql/stackql.", + "For stackql/stackql, what's the success vs failure breakdown of workflow runs on the main branch?", + "Which workflows in stackql/stackql have failed most recently, and on which branches?", + "Are there any in-progress or queued workflow runs for stackql/stackql right now?", + ], + } +} + +fn audit() -> Persona { + Persona { + title: "Agentic Audit (IGA, entitlements, CSPM, FinOps)", + preamble: format!( + "You are a compliance and audit agent covering identity governance (IGA), \ + entitlements, cloud security posture (CSPM), and cost (FinOps). You run \ + point-in-time checks over the live estate and report findings an auditor \ + could act on: who has access to what, where security posture is weak, and \ + where configuration violates policy. State each finding with the SQL and \ + the rows behind it so it is verifiable and re-runnable. You assess and \ + evidence; you never change configuration. When a check needs credentials \ + the current session does not have (private org membership, AWS posture, \ + billing/cost), say so and describe what the credentialed query would be.\n{STACKQL_GUIDANCE}" + ), + examples: &[ + "Who are the public members of the stackql org, and are any of them org owners?", + "Audit branch protection across the stackql org: which default branches are unprotected?", + "Which public repos in the stackql org have no license declared (a compliance risk)?", + "Give me a point-in-time inventory of the stackql org's repos with visibility and last-updated date.", + ], + } +} diff --git a/talk.md b/talk.md new file mode 100644 index 0000000..556e802 --- /dev/null +++ b/talk.md @@ -0,0 +1,289 @@ +# Talk kit: Rust-native agentic platform engineering with embedded MCP + +Presentation content and demo runbook for the meetup talk. Speaker is Jeff Aven (StackQL, OSS). The whole stack is MIT-licensed and public. + +The submitted abstract: + +> Title: Rust-native agentic platform engineering with embedded MCP +> +> Building Rust-native agents for platform engineering and observability, with the MCP server embedded and vendored straight into the Rust application. Demonstrated with concrete examples: stackql-agent, one binary that becomes a platform-engineering, SRE, or audit agent by swapping a system prompt, and auditron, a compliance copilot that ships as one self-contained binary with no runtime downloads. + +The deck has six sections (a-f). Each section below lists the slides, the bullets that go on them, and speaker notes (the things to say out loud, not put on the slide). Demo runbook with exact commands and a fallback plan is at the end. + +Style for slides: short bullets, one idea each. The prose here is the script, not the slide text. + +## a) Intro to StackQL + +### Slide a1: The problem + +- Cloud and SaaS state lives behind dozens of APIs: AWS, GitHub, Google, Azure, Kubernetes, Okta, and on. +- Every one has its own SDK, auth, pagination, and quirks. +- Answering a flat question ("which repos have no branch protection", "what failed last night", "who has admin") means writing bespoke glue against each API. + +Speaker notes: this is the daily reality of platform, SRE, and security teams. The data exists; getting at it uniformly is the tax. + +### Slide a2: StackQL is cloud-as-SQL + +- StackQL lets you query and provision cloud and SaaS resources using plain SQL. +- Providers become schemas; resources become tables: `github.repos.repos`, `aws.ec2.instances`, `google.compute.instances`. +- `SELECT` to read state. `INSERT` / `DELETE` / `EXEC` to provision and manage. +- No server or database to operate: provider definitions are derived from OpenAPI, and a SQLite-backed engine turns SQL into REST calls at query time. + +Speaker notes: think "the cloud as a database you never have to load". The engine is stateless; it calls the live APIs under the hood and maps the responses into rows. Provider definitions are open and community-extensible. + +### Slide a3: A query is just a query + +```sql +SELECT name, default_branch, visibility +FROM github.repos.repos +WHERE org = 'stackql' AND archived = 0; +``` + +- One SQL surface over everything. The same shape whether it is GitHub repos or AWS security groups. +- Joins across providers work too: AWS resources against the GitHub repo that deploys them, for example. + +Speaker notes: the point for today is that this is a uniform, structured, machine-friendly interface. Hold that thought, because it is exactly what an LLM wants. + +## b) Intro to StackQL MCP + +### Slide b1: MCP in one line + +- Model Context Protocol: the standard way to give an LLM tools it can call. Think of it as the USB-C port for AI tooling. +- A model talks to an MCP server; the server exposes tools, the model calls them, results come back as structured content. + +Speaker notes: keep this short, most of the room knows MCP. The interesting bit is what we put behind it. + +### Slide b2: The StackQL MCP server + +- Serves StackQL over MCP: `stackql mcp`. +- 13 tools, in three groups: + - Discovery: `list_providers`, `list_services`, `list_resources`, `describe_resource`, `describe_method`, `list_methods`, `list_registry`, `server_info` + - Query: `run_select_query`, `validate_select_query` + - Change: `run_mutation_query`, `run_lifecycle_operation`, `pull_provider` +- Safety is a server-side contract, not a client promise: `read_only`, `safe`, `delete_safe`, `full_access`. Default is the most restrictive. + +Speaker notes: the key idea: instead of writing N bespoke tools for N APIs, the agent gets one SQL-shaped tool surface over the entire cloud and SaaS estate. And read_only is enforced inside the server, so an agent literally cannot mutate when you start it read_only, no matter what the model decides. + +## c) Agentic use cases for StackQL + +### Slide c1: Why SQL and agents fit + +- One tool surface for every provider: the model does not need a different tool per API. +- The model already speaks SQL fluently. No bespoke tool schema to teach. +- Results are tabular and structured: clean grounding, easy to cite, easy to verify. +- `read_only` is a real, enforceable safety boundary for letting an agent loose on production data. + +Speaker notes: a lot of agent tool design is fighting the surface area. Here the surface area is one well-understood query language and the model is already good at it. + +### Slide c2: Three pillars, one agent + +- Agentic platform engineering: estate consistency and golden-path conformance. "Which repos drift from our standard: no branch protection, no CI, no required reviews?" +- Agentic SRE: live operational signal and triage. "What failed recently, on which branch, and what changed?" +- Agentic audit: IGA and entitlements (who can do what), CSPM (security posture), FinOps (cost). Point-in-time checks with evidence. + +Speaker notes: these look like three different products. They are not. Watch the demo: it is one binary and one agent. The only thing that changes between a platform-engineering agent and an SRE agent is the system prompt. The backend, the tools, the safety mode are identical. + +### Slide c3: The shape of an agentic answer + +- Natural-language question in. +- Agent decides which StackQL queries to run, runs them via MCP, reads the rows. +- Answer out, grounded in the SQL it actually executed (which we show on screen). + +Speaker notes: this is not text-to-SQL with one query. It is a loop: discover schema if needed, query, look at results, query again, then answer. The multi-turn tool loop is where "agent" earns the name. + +## d) Embedded and vendored MCP: motivation and Rust primitives + +This is the technical heart of the talk. + +### Slide d1: The usual MCP topology, and why it is wrong for shipped software + +- Normal MCP: the server is a separate process or service you deploy, and clients connect to it over a network. +- Great for shared, multi-tenant capability. +- Bad for distributable agent software: now your users install, secure, version, and operate two things instead of one. The server is a moving part you do not control on their machine. + +Speaker notes: if you are building an agent that other people run, the deployed-server model pushes your problems onto them. + +### Slide d2: Invert it: embed the server in your binary + +- The agent process owns the server lifecycle: acquire the `stackql` binary, spawn it as a child over stdio, complete the MCP handshake, hand back a connected client. +- The server becomes an implementation detail of your app, not a dependency your users manage. +- Two acquisition modes behind one API: + - Sidecar (default): download the platform `.mcpb` bundle at first run, verify its sha256 against pins baked into the crate, cache it. Offline after first run. + - Vendored: `include_bytes!` the bundle into your binary and extract on first run. Zero network at runtime. One artifact to ship. + +Speaker notes: sidecar is the small-download, always-current path. Vendored is the "hand someone a single file and it works on a plane" path. Same API, you pick at build time. + +### Slide d3: The Rust crate + +- `stackql-mcp` on crates.io. The dependency surface is deliberately tiny: rmcp, ureq, zip, sha2, serde. + +```rust +use stackql_mcp::{Mode, StackqlMcp}; + +let server = StackqlMcp::builder() + .mode(Mode::ReadOnly) + .auth(serde_json::json!({ "github": { "type": "null_auth" } })) + .start() // acquires + spawns + MCP handshake + .await?; // -> RunningServer, derefs to a connected rmcp client +``` + +- Vendoring is a macro plus a build-time env var: + +```rust +let server = StackqlMcp::builder() + .bundle_bytes(stackql_mcp::include_bundle!()) // embedded at compile time + .start().await?; +``` + +Speaker notes: `start()` is the whole acquisition state machine: resolve the platform, check the shared cache, download and verify if needed (or use the embedded bytes), extract, spawn, handshake. The caller sees one call and gets a connected client. + +### Slide d4: Wiring it into an agent (rig + rmcp) + +- The Rust agent ecosystem we stand on: `rig` (agent framework, 0.38) and `rmcp` (the official Rust MCP SDK, 1.7). `stackql-mcp` hands back an rmcp client, which is exactly what rig consumes. + +```rust +// embedded StackQL server -> rig agent, in about ten lines +let server = StackqlMcp::builder().mode(Mode::ReadOnly).auth(auth) + .bundle_bytes(stackql_mcp::include_bundle!()).start().await?; + +let tools = server.list_all_tools().await?; // the 13 StackQL tools +let sink = server.peer().to_owned(); // the rmcp client peer + +let agent = anthropic::Client::from_env()? + .agent(anthropic::completion::CLAUDE_OPUS_4_8) + .preamble(PERSONA_SYSTEM_PROMPT) // <- the only thing that changes + .rmcp_tools(tools, sink) // rig consumes rmcp directly + .default_max_turns(20) + .build(); + +let answer = agent.prompt("which repos lack branch protection?").await?; +``` + +Speaker notes: that is the entire integration. `list_all_tools()` and `peer()` come straight off our embedded client; rig's `rmcp_tools()` takes them as-is because we are both on rmcp 1.7. The persona is a string. Everything else is identical across the three agents. + +### Slide d5: Why Rust for this + +- One static binary, no runtime, fast cold start. The vendoring story (`include_bytes!`) is native to the language, not a hack. +- Memory safety matters for a thing that spawns child processes and parses untrusted zip and JSON. +- The ecosystem is here now: rig for agents, rmcp as the official MCP SDK. This is not a toy. + +Speaker notes: the "single self-contained binary that is also an AI agent over your entire cloud estate" is a genuinely Rust-shaped artifact. You would fight the language to do this in most others. + +## e) Demo + +Run the live demo here. Full runbook with exact commands and fallbacks is in the appendix below. The arc: + +1. Pre-flight: `stackql-agent --check`. Show it start the embedded server and list the 13 tools. No API key, no cloud creds. +2. Platform persona: ask about branch protection or missing licenses across the org. Watch it call `run_select_query` live (the tool calls print as it goes), then answer grounded in the rows. +3. SRE persona: same binary, `--persona sre`. Ask about recent failed workflow runs. Point out: different agent, identical backend, the system prompt is the only change. +4. Audit persona: `--persona audit`. Ask for unprotected default branches or public org members. Frame IGA/CSPM/FinOps on real credentials as the same code pointed at a credentialed provider. +5. The single-binary reveal: `ls -lh` the vendored release binary, then run it from a clean home directory with no network and no credentials. It still works, because the server is inside it. +6. Optional counterpoint: auditron, the deterministic sibling. When you need reproducible evidence rather than a conversation, the same embedded backend runs YAML control packs and emits an auditor-ready evidence zip. + +Speaker notes: the emotional beat is step 3, the persona swap. Build to it. The technical beat is step 5, pulling the network and watching an 80-120 MB binary still answer questions about a live cloud estate. + +## f) The ask + +- It is all open source, MIT. +- `cargo add stackql-mcp`. Docs on docs.rs. +- Repos: + - stackql/stackql-mcp-rs: the crate, plus the stackql-agent and auditron demos and the control packs + - stackql/stackql: the engine +- Ways to get involved: + - Star the repos if this is useful (it genuinely helps). + - Try the agent against your own org or cloud account. + - Contribute: control packs are community-extensible YAML, provider coverage is open, and new agent personas are a pull request against one file. +- One line to close on: a platform engineer, an SRE, and an auditor, in a single binary, that you can hand to someone on a plane. + +Speaker notes: be specific about the ask. "Star the repo and try it against your own GitHub org tonight, it needs zero credentials" is a concrete call to action the room can act on. + +## Appendix: demo runbook + +Rehearse this end to end at least twice before the talk. The failure modes are network and rate limits, both of which have mitigations below. + +### Pre-flight (do this before you walk on stage) + +```sh +# 1. Build the vendored single binary once, ahead of time. +export ANTHROPIC_API_KEY=sk-ant-... # your key +BUNDLE=$(cargo run -q -p stackql-mcp --example fetch_bundle) +STACKQL_MCP_BUNDLE_FILE=$BUNDLE cargo build -p stackql-agent --features vendored --release +AGENT=target/release/stackql-agent # the artifact you will demo + +# 2. Warm the provider cache and prove the machine is ready (no model calls). +$AGENT --check --persona platform + +# 3. Warm the GitHub data path so the first real query is instant and to avoid +# the unauthenticated rate limit biting mid-demo. A GitHub token lifts the +# limit from 60/hour to 5000/hour - strongly recommended on conference wifi. +export GITHUB_TOKEN=ghp_... # optional but recommended +$AGENT --persona platform -p "list 3 repos in the stackql org with their visibility" +``` + +### The three persona runs (exact commands) + +Platform engineering: + +```sh +target/release/stackql-agent --persona platform +# then type: +# Which repos in the stackql org are missing branch protection on their default branch? +# Which stackql org repos have no license, and why is that a problem? +``` + +SRE: + +```sh +target/release/stackql-agent --persona sre +# then type: +# Show the most recent failed GitHub Actions workflow runs for stackql/stackql. +# What's the pass/fail breakdown on the main branch? +``` + +Audit: + +```sh +target/release/stackql-agent --persona audit +# then type: +# Audit branch protection across the stackql org: which default branches are unprotected? +# Who are the public members of the stackql org, and are any org owners? +``` + +One-shot mode (good for a scripted, deterministic demo where you do not want to type live): + +```sh +target/release/stackql-agent --persona sre -p "Show the most recent failed workflow runs for stackql/stackql" +``` + +### What runs live vs what is the credentialed extension + +Be honest about this on stage; it is a strength, not a weakness. + +- Live, zero credentials (GitHub null_auth, public data): + - Platform engineering: repo config, branch protection, default branches, licenses, staleness. + - SRE: GitHub Actions workflow runs, conclusions, branches. + - Audit posture: branch protection, public org membership, license compliance. +- Credentialed extension (same code, point it at a provider with auth): + - IGA and entitlements over private org membership, teams, and repo collaborators. + - CSPM over code scanning, secret scanning, and Dependabot alerts. + - FinOps over billing and cost. + - AWS, Google, Azure: same agent, `--auth` with the provider's auth document. + +The line to say: "everything you are seeing needs no credentials at all, because it is public GitHub data. Point the exact same binary at a credentialed provider and you have IGA, CSPM, and FinOps over your real estate. The agent does not change." + +### Fallback plan + +- No or flaky wifi: the vendored binary needs no network to start (the server is embedded). It does need network to reach the GitHub API and the Anthropic API. If wifi is dead, fall back to a screen recording or asciinema of a rehearsed run. Record this in advance regardless. +- GitHub rate limit (HTTP 403): unauthenticated is 60/hour per IP. Set `GITHUB_TOKEN` before the talk to get 5000/hour. If you still hit it, switch to the one-shot commands you warmed during pre-flight (cached) or to auditron, which makes fewer calls. +- Anthropic API hiccup: switch to auditron, the deterministic sibling. It runs the same embedded backend with no LLM in the path and produces the evidence zip, so you still demo embedded MCP and cloud-as-SQL without depending on a model endpoint. +- A query comes back empty and surprising: that can mean a provider error surfaced as an empty result set, not a real "all clear". The audit persona is told about this; if it happens, say so and run a broader query. (This is also a known upstream issue we have filed: tool results should distinguish empty from errored.) + +### Pre-flight checklist (tick before you start) + +- [ ] Vendored release binary built and on PATH or at a known path. +- [ ] `ANTHROPIC_API_KEY` exported in the demo shell. +- [ ] `GITHUB_TOKEN` exported (lifts the rate limit). +- [ ] `stackql-agent --check` run once on the demo machine (warms the cache, proves it works). +- [ ] Each persona's first prompt run once during pre-flight (warms data, confirms answers). +- [ ] Screen recording of a full rehearsed run saved locally as the ultimate fallback. +- [ ] Font size up, terminal colors high-contrast, tool-call traces visible. From f53e469ae2e85c9cda6f21282c52351bdd75929b Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Thu, 18 Jun 2026 05:27:25 +1000 Subject: [PATCH 2/4] demo updates --- pr.md | 261 -------------------------------------------------- talk.md | 289 -------------------------------------------------------- 2 files changed, 550 deletions(-) delete mode 100644 pr.md delete mode 100644 talk.md diff --git a/pr.md b/pr.md deleted file mode 100644 index 871bb5e..0000000 --- a/pr.md +++ /dev/null @@ -1,261 +0,0 @@ -# stackql-mcp launch kit - -Working drafts for the crates.io launch of `stackql-mcp` and the auditron demo. Blog publishes first; the Reddit and This Week in Rust posts point back to it. - -Placeholders to fill before posting: - -- `BLOG_URL` - final URL of the blog post on stackql.io -- `ASCIINEMA_URL` - recording of the auditron TUI scan - -## a) Blog post (Docusaurus) - -Suggested path: `blog/2026-06-embedded-mcp-rust.md` - -~~~markdown ---- -title: An embedded StackQL MCP server for Rust agents -description: Query and provision cloud infrastructure over SQL from inside your Rust agent process, with no external server to deploy. Plus auditron, a compliance copilot in a single binary. -authors: [jeffreyaven] -tags: [mcp, rust, agents, compliance] ---- - -StackQL exposes cloud and SaaS providers (AWS, GitHub, Google, Azure, and -more) as SQL tables: `SELECT` to query your estate, `INSERT`/`DELETE` to -provision and tear down. The StackQL MCP server makes that capability -available to AI agents over the Model Context Protocol. - -Today we are releasing [stackql-mcp](https://crates.io/crates/stackql-mcp), -a Rust crate that embeds that server inside your agent process. No -deployment, no sidecar to operate, no network dependency at runtime if you -choose so. It joins the npm and PyPI wrappers in the embedded MCP family, -sharing the same binary cache and the same launch contract. - - - -## Why embedded - -MCP servers are usually deployed as standalone processes that agents -connect to. That is the right shape for shared, multi-tenant capability, -but it is a poor fit for distributable agent software: your users now have -two things to install, version, and secure. - -The embedded model inverts this. Your Rust binary owns the server -lifecycle: it acquires the `stackql` binary, launches it as a child over -stdio, completes the MCP handshake, and hands you a connected client from -[rmcp](https://crates.io/crates/rmcp), the official Rust MCP SDK. The -server is an implementation detail of your app. - -```rust -use stackql_mcp::{Mode, StackqlMcp}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let server = StackqlMcp::builder() - .mode(Mode::ReadOnly) - .auth(serde_json::json!({"github": {"type": "null_auth"}})) - .start() - .await?; - let tools = server.list_all_tools().await?; - println!("{} tools available", tools.len()); - server.shutdown().await?; - Ok(()) -} -``` - -Two acquisition modes sit behind that one API: - -- **Sidecar** (the default): the platform's `.mcpb` bundle is downloaded at - first run, verified against sha256 pins baked into the crate at release - time, and cached under `~/.stackql/mcp-server-bin/` - the same cache the - npm and PyPI wrappers use. Subsequent starts are offline. -- **Vendored**: the bundle is embedded in your binary with - `include_bytes!` and extracted on first run. No network at runtime, one - artifact to ship. - -```rust -let server = StackqlMcp::builder() - .bundle_bytes(stackql_mcp::include_bundle!()) - .start() - .await?; -``` - -Safety is a first-class part of the contract. The server enforces a mode -per session (`read_only`, `safe`, `delete_safe`, `full_access`) and the -crate defaults to the most restrictive; escalation is an explicit caller -opt-in. - -## auditron: an auditor in a single binary - -To show what the vendored mode is for, the repo ships -[auditron](https://github.com/stackql/stackql-mcp-rs/tree/main/auditron), -a terminal compliance copilot. The business problem: compliance engineers -run point-in-time control checks and need auditor-ready evidence, and the -usual answer involves cloud consoles and screenshots. - -auditron runs a YAML control pack (id, description, SQL, pass criteria per -control) through the embedded server in read-only mode and streams -pass/fail/error into a ratatui table as results arrive. The SQL that -produced each finding is always on screen. Select a finding and press `e` -and Claude explains it and drafts remediation steps. - -[ASCIINEMA_URL] - -Then there is the part the auditor actually wants: - -```sh -auditron evidence --out evidence-2026-06.zip -``` - -The zip contains the run manifest (pack checksum, collector identity, -server version, timings), the exact pack source and per-control SQL, and -per-control CSVs. Another auditor can re-run the same pack against the -same estate and diff the results. - -The demo pack checks GitHub org security posture (branch protection, -default branches, licenses, staleness) using the github provider in -`null_auth` mode, so it runs against public org data with zero -credentials. Point it at your own org: - -```sh -auditron scan --var org=your-org -``` - -Control packs are data, not code. They live in the repo as -`controls/*.yaml` and are community extensible. - -## Lessons from building on live cloud APIs - -Two things we hit while building the demo are worth knowing if you build -on this stack: - -First, MCP tool results from the server carry the typed payload in -`structuredContent` and a markdown rendering in the text content. Read the -structured payload; parsing the markdown is a mistake we made so you do -not have to. - -Second, provider API errors (rate limits especially) can surface as empty -result sets rather than tool errors. For a compliance tool, where "no rows" -means "compliant", that distinction is everything. The github pack includes -an inventory control that must return rows, doubling as a canary for the -whole run; we have also raised the underlying behavior upstream. - -## Getting started - -```sh -cargo add stackql-mcp -``` - -The crate is on [crates.io](https://crates.io/crates/stackql-mcp) with API -docs on [docs.rs](https://docs.rs/stackql-mcp). The source, the auditron -demo, and the control packs are at -[stackql/stackql-mcp-rs](https://github.com/stackql/stackql-mcp-rs), and -the engine itself lives at -[stackql/stackql](https://github.com/stackql/stackql). Issues and control -pack contributions welcome. -~~~ - -## b) Community posts - -### Reddit (r/rust) - -Title: - -~~~text -stackql-mcp: embed a cloud-querying MCP server in your Rust agent (and auditron, a compliance TUI in a single 80MB binary) -~~~ - -Body: - -~~~markdown -We just published [stackql-mcp](https://crates.io/crates/stackql-mcp), a -crate that embeds the StackQL MCP server (cloud infrastructure as SQL: -AWS, GitHub, Google, Azure...) inside a Rust process. Builder API in, -connected [rmcp](https://crates.io/crates/rmcp) client out: - - let server = StackqlMcp::builder() - .mode(Mode::ReadOnly) - .auth(json!({"github": {"type": "null_auth"}})) - .start() - .await?; - -Things r/rust might find interesting: - -- Two acquisition modes behind one API: sidecar (download at first run, - sha256-verified against pins baked into the crate, shared cache) and - vendored (`include_bytes!` the server bundle, extract on first run). - The vendored mode is the fun one: your agent plus a full SQL engine for - cloud APIs in one shippable binary, no network needed at runtime. -- The demo app, auditron, is a ratatui compliance copilot: it streams - control checks (defined as YAML with SQL and pass criteria) into a live - table, always shows the SQL behind a finding, has Claude draft - remediation steps on demand, and exports an auditor-ready evidence zip - (manifest, exact SQL, per-control CSVs - re-runnable by an auditor). - The github demo pack runs unauthenticated against public org data, so - `cargo run -p auditron -- scan` works with zero credentials. -- Dependency surface is deliberately small: rmcp, ureq, zip, sha2, - serde. Server modes (read_only by default) are enforced server-side, - not by the client promising to behave. - -Blog with the design rationale: BLOG_URL -Repo (crate + demo + control packs): https://github.com/stackql/stackql-mcp-rs -The engine: https://github.com/stackql/stackql - -Feedback welcome, especially on the builder API surface and the vendored -build flow (compile-time env var + `include_bundle!` macro - we went back -and forth on build.rs vs explicit pipeline and chose explicit). -~~~ - -### This Week in Rust - -For the "Project/Tooling Updates" section (PR adding a line to the next -draft in [rust-lang/this-week-in-rust](https://github.com/rust-lang/this-week-in-rust)): - -~~~markdown -* [stackql-mcp](https://github.com/stackql/stackql-mcp-rs) - a new crate - embedding the StackQL MCP server (query AWS, GitHub, Google and more - over SQL) in Rust agentic apps, with sha256-pinned sidecar download or - a fully vendored single-binary mode; ships with - [auditron](BLOG_URL), a ratatui compliance copilot demo that produces - auditor-ready evidence packs -~~~ - -## c) Meetup talk synopsis - -Title: - -~~~text -An auditor in a single binary: embedded MCP backends for native Rust agents -~~~ - -Abstract (~150 words): - -~~~text -AI agents are mostly wired together from hosted services: a model API -here, an MCP server deployed there. This talk makes the case for the -opposite shape: a native Rust agent that ships as one binary and owns its -entire backend. - -We build up in three layers. First, the anatomy of a native Rust agent: -tokio, the official rmcp MCP SDK, and a child process speaking MCP over -stdio. Second, the embedded backend: stackql-mcp, a crate that embeds -StackQL (cloud infrastructure as SQL) with two acquisition modes - -sha256-pinned sidecar download, or a server bundle vendored into the -binary with include_bytes!, including what it takes to make extraction, -caching, and process lifecycle boring and reliable. Third, a real agent -on top: auditron, a terminal compliance copilot that runs SQL control -packs against live cloud APIs, explains findings with an LLM, and emits -re-runnable, auditor-ready evidence zips. - -Live demo: zero credentials, one binary, real findings. -~~~ - -Talk notes (not for publication): - -- Demo flow: `auditron scan` TUI against a public GitHub org (null_auth, - no setup on conference wifi beyond GitHub API reachability; mind the - 60 req/h unauthenticated rate limit - warm the cache or bring a token) -- War stories section maps to the upstream issues: markdown vs - structuredContent, boolean typing across resources, errors surfacing as - empty result sets and the canary-control mitigation -- Closing slide: cargo add stackql-mcp, repo links, control packs as the - community contribution surface diff --git a/talk.md b/talk.md deleted file mode 100644 index 556e802..0000000 --- a/talk.md +++ /dev/null @@ -1,289 +0,0 @@ -# Talk kit: Rust-native agentic platform engineering with embedded MCP - -Presentation content and demo runbook for the meetup talk. Speaker is Jeff Aven (StackQL, OSS). The whole stack is MIT-licensed and public. - -The submitted abstract: - -> Title: Rust-native agentic platform engineering with embedded MCP -> -> Building Rust-native agents for platform engineering and observability, with the MCP server embedded and vendored straight into the Rust application. Demonstrated with concrete examples: stackql-agent, one binary that becomes a platform-engineering, SRE, or audit agent by swapping a system prompt, and auditron, a compliance copilot that ships as one self-contained binary with no runtime downloads. - -The deck has six sections (a-f). Each section below lists the slides, the bullets that go on them, and speaker notes (the things to say out loud, not put on the slide). Demo runbook with exact commands and a fallback plan is at the end. - -Style for slides: short bullets, one idea each. The prose here is the script, not the slide text. - -## a) Intro to StackQL - -### Slide a1: The problem - -- Cloud and SaaS state lives behind dozens of APIs: AWS, GitHub, Google, Azure, Kubernetes, Okta, and on. -- Every one has its own SDK, auth, pagination, and quirks. -- Answering a flat question ("which repos have no branch protection", "what failed last night", "who has admin") means writing bespoke glue against each API. - -Speaker notes: this is the daily reality of platform, SRE, and security teams. The data exists; getting at it uniformly is the tax. - -### Slide a2: StackQL is cloud-as-SQL - -- StackQL lets you query and provision cloud and SaaS resources using plain SQL. -- Providers become schemas; resources become tables: `github.repos.repos`, `aws.ec2.instances`, `google.compute.instances`. -- `SELECT` to read state. `INSERT` / `DELETE` / `EXEC` to provision and manage. -- No server or database to operate: provider definitions are derived from OpenAPI, and a SQLite-backed engine turns SQL into REST calls at query time. - -Speaker notes: think "the cloud as a database you never have to load". The engine is stateless; it calls the live APIs under the hood and maps the responses into rows. Provider definitions are open and community-extensible. - -### Slide a3: A query is just a query - -```sql -SELECT name, default_branch, visibility -FROM github.repos.repos -WHERE org = 'stackql' AND archived = 0; -``` - -- One SQL surface over everything. The same shape whether it is GitHub repos or AWS security groups. -- Joins across providers work too: AWS resources against the GitHub repo that deploys them, for example. - -Speaker notes: the point for today is that this is a uniform, structured, machine-friendly interface. Hold that thought, because it is exactly what an LLM wants. - -## b) Intro to StackQL MCP - -### Slide b1: MCP in one line - -- Model Context Protocol: the standard way to give an LLM tools it can call. Think of it as the USB-C port for AI tooling. -- A model talks to an MCP server; the server exposes tools, the model calls them, results come back as structured content. - -Speaker notes: keep this short, most of the room knows MCP. The interesting bit is what we put behind it. - -### Slide b2: The StackQL MCP server - -- Serves StackQL over MCP: `stackql mcp`. -- 13 tools, in three groups: - - Discovery: `list_providers`, `list_services`, `list_resources`, `describe_resource`, `describe_method`, `list_methods`, `list_registry`, `server_info` - - Query: `run_select_query`, `validate_select_query` - - Change: `run_mutation_query`, `run_lifecycle_operation`, `pull_provider` -- Safety is a server-side contract, not a client promise: `read_only`, `safe`, `delete_safe`, `full_access`. Default is the most restrictive. - -Speaker notes: the key idea: instead of writing N bespoke tools for N APIs, the agent gets one SQL-shaped tool surface over the entire cloud and SaaS estate. And read_only is enforced inside the server, so an agent literally cannot mutate when you start it read_only, no matter what the model decides. - -## c) Agentic use cases for StackQL - -### Slide c1: Why SQL and agents fit - -- One tool surface for every provider: the model does not need a different tool per API. -- The model already speaks SQL fluently. No bespoke tool schema to teach. -- Results are tabular and structured: clean grounding, easy to cite, easy to verify. -- `read_only` is a real, enforceable safety boundary for letting an agent loose on production data. - -Speaker notes: a lot of agent tool design is fighting the surface area. Here the surface area is one well-understood query language and the model is already good at it. - -### Slide c2: Three pillars, one agent - -- Agentic platform engineering: estate consistency and golden-path conformance. "Which repos drift from our standard: no branch protection, no CI, no required reviews?" -- Agentic SRE: live operational signal and triage. "What failed recently, on which branch, and what changed?" -- Agentic audit: IGA and entitlements (who can do what), CSPM (security posture), FinOps (cost). Point-in-time checks with evidence. - -Speaker notes: these look like three different products. They are not. Watch the demo: it is one binary and one agent. The only thing that changes between a platform-engineering agent and an SRE agent is the system prompt. The backend, the tools, the safety mode are identical. - -### Slide c3: The shape of an agentic answer - -- Natural-language question in. -- Agent decides which StackQL queries to run, runs them via MCP, reads the rows. -- Answer out, grounded in the SQL it actually executed (which we show on screen). - -Speaker notes: this is not text-to-SQL with one query. It is a loop: discover schema if needed, query, look at results, query again, then answer. The multi-turn tool loop is where "agent" earns the name. - -## d) Embedded and vendored MCP: motivation and Rust primitives - -This is the technical heart of the talk. - -### Slide d1: The usual MCP topology, and why it is wrong for shipped software - -- Normal MCP: the server is a separate process or service you deploy, and clients connect to it over a network. -- Great for shared, multi-tenant capability. -- Bad for distributable agent software: now your users install, secure, version, and operate two things instead of one. The server is a moving part you do not control on their machine. - -Speaker notes: if you are building an agent that other people run, the deployed-server model pushes your problems onto them. - -### Slide d2: Invert it: embed the server in your binary - -- The agent process owns the server lifecycle: acquire the `stackql` binary, spawn it as a child over stdio, complete the MCP handshake, hand back a connected client. -- The server becomes an implementation detail of your app, not a dependency your users manage. -- Two acquisition modes behind one API: - - Sidecar (default): download the platform `.mcpb` bundle at first run, verify its sha256 against pins baked into the crate, cache it. Offline after first run. - - Vendored: `include_bytes!` the bundle into your binary and extract on first run. Zero network at runtime. One artifact to ship. - -Speaker notes: sidecar is the small-download, always-current path. Vendored is the "hand someone a single file and it works on a plane" path. Same API, you pick at build time. - -### Slide d3: The Rust crate - -- `stackql-mcp` on crates.io. The dependency surface is deliberately tiny: rmcp, ureq, zip, sha2, serde. - -```rust -use stackql_mcp::{Mode, StackqlMcp}; - -let server = StackqlMcp::builder() - .mode(Mode::ReadOnly) - .auth(serde_json::json!({ "github": { "type": "null_auth" } })) - .start() // acquires + spawns + MCP handshake - .await?; // -> RunningServer, derefs to a connected rmcp client -``` - -- Vendoring is a macro plus a build-time env var: - -```rust -let server = StackqlMcp::builder() - .bundle_bytes(stackql_mcp::include_bundle!()) // embedded at compile time - .start().await?; -``` - -Speaker notes: `start()` is the whole acquisition state machine: resolve the platform, check the shared cache, download and verify if needed (or use the embedded bytes), extract, spawn, handshake. The caller sees one call and gets a connected client. - -### Slide d4: Wiring it into an agent (rig + rmcp) - -- The Rust agent ecosystem we stand on: `rig` (agent framework, 0.38) and `rmcp` (the official Rust MCP SDK, 1.7). `stackql-mcp` hands back an rmcp client, which is exactly what rig consumes. - -```rust -// embedded StackQL server -> rig agent, in about ten lines -let server = StackqlMcp::builder().mode(Mode::ReadOnly).auth(auth) - .bundle_bytes(stackql_mcp::include_bundle!()).start().await?; - -let tools = server.list_all_tools().await?; // the 13 StackQL tools -let sink = server.peer().to_owned(); // the rmcp client peer - -let agent = anthropic::Client::from_env()? - .agent(anthropic::completion::CLAUDE_OPUS_4_8) - .preamble(PERSONA_SYSTEM_PROMPT) // <- the only thing that changes - .rmcp_tools(tools, sink) // rig consumes rmcp directly - .default_max_turns(20) - .build(); - -let answer = agent.prompt("which repos lack branch protection?").await?; -``` - -Speaker notes: that is the entire integration. `list_all_tools()` and `peer()` come straight off our embedded client; rig's `rmcp_tools()` takes them as-is because we are both on rmcp 1.7. The persona is a string. Everything else is identical across the three agents. - -### Slide d5: Why Rust for this - -- One static binary, no runtime, fast cold start. The vendoring story (`include_bytes!`) is native to the language, not a hack. -- Memory safety matters for a thing that spawns child processes and parses untrusted zip and JSON. -- The ecosystem is here now: rig for agents, rmcp as the official MCP SDK. This is not a toy. - -Speaker notes: the "single self-contained binary that is also an AI agent over your entire cloud estate" is a genuinely Rust-shaped artifact. You would fight the language to do this in most others. - -## e) Demo - -Run the live demo here. Full runbook with exact commands and fallbacks is in the appendix below. The arc: - -1. Pre-flight: `stackql-agent --check`. Show it start the embedded server and list the 13 tools. No API key, no cloud creds. -2. Platform persona: ask about branch protection or missing licenses across the org. Watch it call `run_select_query` live (the tool calls print as it goes), then answer grounded in the rows. -3. SRE persona: same binary, `--persona sre`. Ask about recent failed workflow runs. Point out: different agent, identical backend, the system prompt is the only change. -4. Audit persona: `--persona audit`. Ask for unprotected default branches or public org members. Frame IGA/CSPM/FinOps on real credentials as the same code pointed at a credentialed provider. -5. The single-binary reveal: `ls -lh` the vendored release binary, then run it from a clean home directory with no network and no credentials. It still works, because the server is inside it. -6. Optional counterpoint: auditron, the deterministic sibling. When you need reproducible evidence rather than a conversation, the same embedded backend runs YAML control packs and emits an auditor-ready evidence zip. - -Speaker notes: the emotional beat is step 3, the persona swap. Build to it. The technical beat is step 5, pulling the network and watching an 80-120 MB binary still answer questions about a live cloud estate. - -## f) The ask - -- It is all open source, MIT. -- `cargo add stackql-mcp`. Docs on docs.rs. -- Repos: - - stackql/stackql-mcp-rs: the crate, plus the stackql-agent and auditron demos and the control packs - - stackql/stackql: the engine -- Ways to get involved: - - Star the repos if this is useful (it genuinely helps). - - Try the agent against your own org or cloud account. - - Contribute: control packs are community-extensible YAML, provider coverage is open, and new agent personas are a pull request against one file. -- One line to close on: a platform engineer, an SRE, and an auditor, in a single binary, that you can hand to someone on a plane. - -Speaker notes: be specific about the ask. "Star the repo and try it against your own GitHub org tonight, it needs zero credentials" is a concrete call to action the room can act on. - -## Appendix: demo runbook - -Rehearse this end to end at least twice before the talk. The failure modes are network and rate limits, both of which have mitigations below. - -### Pre-flight (do this before you walk on stage) - -```sh -# 1. Build the vendored single binary once, ahead of time. -export ANTHROPIC_API_KEY=sk-ant-... # your key -BUNDLE=$(cargo run -q -p stackql-mcp --example fetch_bundle) -STACKQL_MCP_BUNDLE_FILE=$BUNDLE cargo build -p stackql-agent --features vendored --release -AGENT=target/release/stackql-agent # the artifact you will demo - -# 2. Warm the provider cache and prove the machine is ready (no model calls). -$AGENT --check --persona platform - -# 3. Warm the GitHub data path so the first real query is instant and to avoid -# the unauthenticated rate limit biting mid-demo. A GitHub token lifts the -# limit from 60/hour to 5000/hour - strongly recommended on conference wifi. -export GITHUB_TOKEN=ghp_... # optional but recommended -$AGENT --persona platform -p "list 3 repos in the stackql org with their visibility" -``` - -### The three persona runs (exact commands) - -Platform engineering: - -```sh -target/release/stackql-agent --persona platform -# then type: -# Which repos in the stackql org are missing branch protection on their default branch? -# Which stackql org repos have no license, and why is that a problem? -``` - -SRE: - -```sh -target/release/stackql-agent --persona sre -# then type: -# Show the most recent failed GitHub Actions workflow runs for stackql/stackql. -# What's the pass/fail breakdown on the main branch? -``` - -Audit: - -```sh -target/release/stackql-agent --persona audit -# then type: -# Audit branch protection across the stackql org: which default branches are unprotected? -# Who are the public members of the stackql org, and are any org owners? -``` - -One-shot mode (good for a scripted, deterministic demo where you do not want to type live): - -```sh -target/release/stackql-agent --persona sre -p "Show the most recent failed workflow runs for stackql/stackql" -``` - -### What runs live vs what is the credentialed extension - -Be honest about this on stage; it is a strength, not a weakness. - -- Live, zero credentials (GitHub null_auth, public data): - - Platform engineering: repo config, branch protection, default branches, licenses, staleness. - - SRE: GitHub Actions workflow runs, conclusions, branches. - - Audit posture: branch protection, public org membership, license compliance. -- Credentialed extension (same code, point it at a provider with auth): - - IGA and entitlements over private org membership, teams, and repo collaborators. - - CSPM over code scanning, secret scanning, and Dependabot alerts. - - FinOps over billing and cost. - - AWS, Google, Azure: same agent, `--auth` with the provider's auth document. - -The line to say: "everything you are seeing needs no credentials at all, because it is public GitHub data. Point the exact same binary at a credentialed provider and you have IGA, CSPM, and FinOps over your real estate. The agent does not change." - -### Fallback plan - -- No or flaky wifi: the vendored binary needs no network to start (the server is embedded). It does need network to reach the GitHub API and the Anthropic API. If wifi is dead, fall back to a screen recording or asciinema of a rehearsed run. Record this in advance regardless. -- GitHub rate limit (HTTP 403): unauthenticated is 60/hour per IP. Set `GITHUB_TOKEN` before the talk to get 5000/hour. If you still hit it, switch to the one-shot commands you warmed during pre-flight (cached) or to auditron, which makes fewer calls. -- Anthropic API hiccup: switch to auditron, the deterministic sibling. It runs the same embedded backend with no LLM in the path and produces the evidence zip, so you still demo embedded MCP and cloud-as-SQL without depending on a model endpoint. -- A query comes back empty and surprising: that can mean a provider error surfaced as an empty result set, not a real "all clear". The audit persona is told about this; if it happens, say so and run a broader query. (This is also a known upstream issue we have filed: tool results should distinguish empty from errored.) - -### Pre-flight checklist (tick before you start) - -- [ ] Vendored release binary built and on PATH or at a known path. -- [ ] `ANTHROPIC_API_KEY` exported in the demo shell. -- [ ] `GITHUB_TOKEN` exported (lifts the rate limit). -- [ ] `stackql-agent --check` run once on the demo machine (warms the cache, proves it works). -- [ ] Each persona's first prompt run once during pre-flight (warms data, confirms answers). -- [ ] Screen recording of a full rehearsed run saved locally as the ultimate fallback. -- [ ] Font size up, terminal colors high-contrast, tool-call traces visible. From b4a88cc456bb2297a10fa84444c39d73a6adb212 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 18 Aug 2026 10:28:35 +1000 Subject: [PATCH 3/4] Bump pinned stackql release to v0.10.601; crate 0.1.1 The v0.10.500 release assets were re-published upstream, so the sha256 pins baked into 0.1.0 no longer verify (conformance test fails with ChecksumMismatch on all platforms). Re-pin to v0.10.601; all four pins match the published .mcpb.sha256 assets. Co-Authored-By: Claude Fable 5 --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/pins.rs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d400b03..27e9be1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2254,7 +2254,7 @@ dependencies = [ [[package]] name = "stackql-mcp" -version = "0.1.0" +version = "0.1.1" dependencies = [ "rmcp", "serde", diff --git a/Cargo.toml b/Cargo.toml index fe45c40..e159ca7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ resolver = "3" [package] name = "stackql-mcp" -version = "0.1.0" +version = "0.1.1" edition = "2021" # Effective MSRV is set by rmcp 1.x (let-chains): 1.88. rust-version = "1.88" diff --git a/src/pins.rs b/src/pins.rs index cde8157..fb94737 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -10,7 +10,7 @@ use crate::platform::Platform; /// The stackql release this crate version pins (release.yaml in the /// packaging repo, leading v stripped). -pub const STACKQL_VERSION: &str = "0.10.500"; +pub const STACKQL_VERSION: &str = "0.10.601"; /// A pinned bundle: name and sha256 as published on the GitHub release. #[derive(Clone, Copy, Debug)] @@ -24,22 +24,22 @@ pub const PINS: &[Pin] = &[ Pin { platform_key: "linux-x64", bundle_name: "stackql-mcp-linux-x64.mcpb", - sha256: "6615737747156b1a8413a976afb23af2e7eec29ebc98a6f0a0f65d1b153c44be", + sha256: "4a1cad1345fba1aae1f31269fd96aebed7a7825b38f6509466c1c995ce114e52", }, Pin { platform_key: "linux-arm64", bundle_name: "stackql-mcp-linux-arm64.mcpb", - sha256: "594bedbabc3096dc3563c907724e845ce0b61a67de4b3fed4158b40c0363786c", + sha256: "518b2be638fa2b31438e39ed7adafc1621d7397f6700016e9f2d516e500d3cd1", }, Pin { platform_key: "windows-x64", bundle_name: "stackql-mcp-windows-x64.mcpb", - sha256: "d2ce895e88f9c6b557df07073158629808f56d75598f3a701164d65506b791b0", + sha256: "35ed2c66ff6aa5551b9fd7976cc6c49e370cb2b619eb2b5047c3bdbd33076eab", }, Pin { platform_key: "darwin-universal", bundle_name: "stackql-mcp-darwin-universal.mcpb", - sha256: "4eed70af5cfa67295ae0b42fa3a6dca71ac9acabd0d67914fd96ad1247a9b4cc", + sha256: "1cb60eba9dbc849ad9d505eb4992d0a348e4088ab51c8856b5206b7cc389995d", }, ]; @@ -102,7 +102,7 @@ mod tests { let pin = pin_for(Platform::LinuxX64).unwrap(); assert_eq!( bundle_url(pin), - "https://github.com/stackql/stackql/releases/download/v0.10.500/stackql-mcp-linux-x64.mcpb" + "https://github.com/stackql/stackql/releases/download/v0.10.601/stackql-mcp-linux-x64.mcpb" ); } } From b0f484724158c49b5b16c609eb536353aaa41f67 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 18 Aug 2026 10:40:54 +1000 Subject: [PATCH 4/4] Sidecar defaults to the latest stackql release; crate 0.2.0 The sidecar now resolves github.com/stackql/stackql/releases/latest at start-up and verifies the download against that release's published .mcpb.sha256 asset, so the crate no longer needs a re-pin every time a new server ships (or, as with v0.10.500, when release assets are re-published). - BundleVersion { Latest (default), Pinned, Exact(v) } via Builder::version() or STACKQL_MCP_VERSION=latest|pinned| - Pinned keeps the baked sha256 pins (offline verification) and is the last-resort fallback; if the latest release cannot be resolved (offline) the newest cached release is used first - fetch_bundle() follows the same selection; fetch_bundle_with() picks explicitly - no-default-features build is now clippy-clean too Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 17 +- Cargo.lock | 6638 +++++++++++++++++++------------------- Cargo.toml | 105 +- README.md | 239 +- examples/fetch_bundle.rs | 2 +- src/acquire.rs | 329 +- src/cache.rs | 1 + src/error.rs | 173 +- src/lib.rs | 604 ++-- src/pins.rs | 23 +- src/release.rs | 234 ++ 11 files changed, 4345 insertions(+), 4020 deletions(-) create mode 100644 src/release.rs diff --git a/CLAUDE.md b/CLAUDE.md index cd52baf..e4720fe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,8 +11,10 @@ stance. Two acquisition modes, both behind one API: -1. Sidecar (default feature): download the platform's .mcpb at first run, - verify sha256 against pins baked into the crate, cache, spawn over stdio +1. Sidecar (default feature): resolve the latest stackql release, download + the platform's .mcpb at first run, verify sha256 against the release's + .sha256 asset (or the pins baked into the crate for + BundleVersion::Pinned), cache, spawn over stdio 2. Vendored (`vendored` feature): caller embeds the binary with `include_bytes!` (we provide the macro/helper + extract-on-first-run) - the single-shippable-binary story for compiled agent apps @@ -27,9 +29,12 @@ justify anything beyond that. Source of truth: stackql/stackql-mcpb-packaging (the packaging repo). -- Per-version sha256 pins from the release .sha256 assets (a consolidated - platforms.json release asset is planned - prefer it once present); pins - are baked at crate build/render time like npm's platforms.json +- Default release is latest: resolved at start-up from + github.com/stackql/stackql/releases/latest and verified against that + release's .sha256 asset (a consolidated platforms.json release asset is + planned - prefer it once present). Per-version sha256 pins are still + baked into the crate for BundleVersion::Pinned and as the offline + fallback; STACKQL_MCP_VERSION=latest|pinned| overrides - Canonical launch args (cwd-independence mandatory): `mcp --mcp.server.type=stdio --approot /.stackql --mcp.config {"server": {"mode": "", "audit": {"disabled": true}}}` @@ -37,7 +42,7 @@ Source of truth: stackql/stackql-mcpb-packaging (the packaging repo). - Shared binary cache: `~/.stackql/mcp-server-bin///` (same as npm/pypi wrappers - check before downloading) - Platform keys: linux-x64, linux-arm64, windows-x64, darwin-universal -- Env overrides honored: STACKQL_MCP_BIN, STACKQL_MCP_BUNDLE +- Env overrides honored: STACKQL_MCP_BIN, STACKQL_MCP_BUNDLE, STACKQL_MCP_VERSION - Conformance: packaging repo's scripts/smoke-test.py --cmd must pass against the crate's example launcher; port the same checks to Rust tests diff --git a/Cargo.lock b/Cargo.lock index 27e9be1..93853ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3319 +1,3319 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "as-any" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063" - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "auditron" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "clap", - "crossterm", - "csv", - "ratatui", - "rmcp", - "serde", - "serde_json", - "serde_yaml", - "sha2", - "stackql-mcp", - "tokio", - "ureq", - "zip", -] - -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "aws-lc-rs" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bitflags" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - -[[package]] -name = "cc" -version = "1.2.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link", -] - -[[package]] -name = "clap" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim 0.11.1", -] - -[[package]] -name = "clap_derive" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" - -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "compact_str" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd622ebbb56a5b2ccb651b32b911cdeb2a9b4b11776b2473bf26a26a286244e" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - -[[package]] -name = "convert_case" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "cookie" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "cookie_store" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" -dependencies = [ - "cookie", - "document-features", - "idna", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "time", - "url", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags", - "crossterm_winapi", - "mio", - "parking_lot", - "rustix", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "csv" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "csv-core" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" -dependencies = [ - "memchr", -] - -[[package]] -name = "darling" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[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 0.11.1", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "data-encoding" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" - -[[package]] -name = "deluxe" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed332aaf752b459088acf3dd4eca323e3ef4b83c70a84ca48fb0ec5305f1488" -dependencies = [ - "deluxe-core", - "deluxe-macros", - "once_cell", - "proc-macro2", - "syn", -] - -[[package]] -name = "deluxe-core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddada51c8576df9d6a8450c351ff63042b092c9458b8ac7d20f89cbd0ffd313" -dependencies = [ - "arrayvec", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn", -] - -[[package]] -name = "deluxe-macros" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87546d9c837f0b7557e47b8bd6eae52c3c223141b76aa233c345c9ab41d9117" -dependencies = [ - "deluxe-core", - "heck 0.4.1", - "if_chain", - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "displaydoc" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - -[[package]] -name = "dunce" -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 = "either" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "eventsource-stream" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" -dependencies = [ - "futures-core", - "nom", - "pin-project-lite", -] - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", - "zlib-rs", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-timer" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "h2" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "http" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "hyper" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "if_chain" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb" - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", -] - -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - -[[package]] -name = "instability" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" -dependencies = [ - "darling", - "indoc", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jni" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" -dependencies = [ - "cfg-if", - "combine", - "jni-macros", - "jni-sys", - "log", - "simd_cesu8", - "thiserror", - "walkdir", - "windows-link", -] - -[[package]] -name = "jni-macros" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", - "simd_cesu8", - "syn", -] - -[[package]] -name = "jni-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" -dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", -] - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" - -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "memchr" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "nanoid" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" -dependencies = [ - "rand 0.8.6", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "ordered-float" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" -dependencies = [ - "num-traits", -] - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pastey" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project" -version = "1.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit 0.25.12+spec-1.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.4", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "ratatui" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" -dependencies = [ - "bitflags", - "cassowary", - "compact_str", - "crossterm", - "indoc", - "instability", - "itertools", - "lru", - "paste", - "strum", - "unicode-segmentation", - "unicode-truncate", - "unicode-width 0.2.0", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[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", -] - -[[package]] -name = "reqwest" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", -] - -[[package]] -name = "rig-core" -version = "0.38.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "557f11c26c2c2ea61d9cb843ce5adff138cc5785f58ff4b87d779de8acaa32ae" -dependencies = [ - "as-any", - "async-stream", - "base64", - "bytes", - "eventsource-stream", - "fastrand", - "futures", - "futures-timer", - "glob", - "http", - "mime", - "mime_guess", - "nanoid", - "ordered-float", - "pin-project-lite", - "reqwest", - "rig-derive", - "rmcp", - "schemars", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-tungstenite", - "tracing", - "tracing-futures", - "url", -] - -[[package]] -name = "rig-derive" -version = "0.38.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643eb495acbd4bd5976164cd068f186d534f741def8d5d052f860266b98d07f8" -dependencies = [ - "convert_case", - "deluxe", - "indoc", - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "serde_json", - "syn", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rmcp" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0810a9f717d9828f475fe1f629f4c305c8464b7f496c3a854b58d29e65f4058e" -dependencies = [ - "async-trait", - "base64", - "chrono", - "futures", - "pastey", - "pin-project-lite", - "rmcp-macros", - "schemars", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", -] - -[[package]] -name = "rmcp-macros" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aefac48c364756e97f04c0401ba3231e8607882c7c1d92da0437dc16307904d" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "serde_json", - "syn", -] - -[[package]] -name = "rustc-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.23.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" -dependencies = [ - "aws-lc-rs", - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "schemars" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" -dependencies = [ - "chrono", - "dyn-clone", - "ref-cast", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - -[[package]] -name = "simd_cesu8" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" -dependencies = [ - "rustc_version", - "simdutf8", -] - -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" - -[[package]] -name = "socket2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "stackql-agent" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "futures", - "rig-core", - "rmcp", - "serde_json", - "stackql-mcp", - "tokio", -] - -[[package]] -name = "stackql-mcp" -version = "0.1.1" -dependencies = [ - "rmcp", - "serde", - "serde_json", - "sha2", - "tokio", - "ureq", - "zip", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" -dependencies = [ - "futures-util", - "log", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tungstenite", - "webpki-roots 0.26.11", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" - -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "toml_datetime 0.6.11", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.25.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" -dependencies = [ - "indexmap", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "winnow 1.0.3", -] - -[[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" -dependencies = [ - "winnow 1.0.3", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", - "url", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "futures", - "futures-task", - "pin-project", - "tracing", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.4", - "rustls", - "rustls-pki-types", - "sha1", - "thiserror", - "utf-8", -] - -[[package]] -name = "typed-path" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" - -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "ureq" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" -dependencies = [ - "base64", - "cookie_store", - "flate2", - "log", - "percent-encoding", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "ureq-proto", - "utf8-zero", - "webpki-roots 1.0.7", -] - -[[package]] -name = "ureq-proto" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" -dependencies = [ - "base64", - "http", - "httparse", - "log", -] - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8-zero" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.4+wasi-0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-streams" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.7", -] - -[[package]] -name = "webpki-roots" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[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-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerofrom" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zip" -version = "8.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" -dependencies = [ - "crc32fast", - "flate2", - "indexmap", - "memchr", - "typed-path", - "zopfli", -] - -[[package]] -name = "zlib-rs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zopfli" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-any" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "auditron" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "crossterm", + "csv", + "ratatui", + "rmcp", + "serde", + "serde_json", + "serde_yaml", + "sha2", + "stackql-mcp", + "tokio", + "ureq", + "zip", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "aws-lc-rs" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "compact_str" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd622ebbb56a5b2ccb651b32b911cdeb2a9b4b11776b2473bf26a26a286244e" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[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 0.11.1", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "deluxe" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed332aaf752b459088acf3dd4eca323e3ef4b83c70a84ca48fb0ec5305f1488" +dependencies = [ + "deluxe-core", + "deluxe-macros", + "once_cell", + "proc-macro2", + "syn", +] + +[[package]] +name = "deluxe-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddada51c8576df9d6a8450c351ff63042b092c9458b8ac7d20f89cbd0ffd313" +dependencies = [ + "arrayvec", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "deluxe-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87546d9c837f0b7557e47b8bd6eae52c3c223141b76aa233c345c9ab41d9117" +dependencies = [ + "deluxe-core", + "heck 0.4.1", + "if_chain", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dunce" +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 = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-timer" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "if_chain" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "instability" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nanoid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" +dependencies = [ + "rand 0.8.6", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.12+spec-1.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools", + "lru", + "paste", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[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", +] + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rig-core" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557f11c26c2c2ea61d9cb843ce5adff138cc5785f58ff4b87d779de8acaa32ae" +dependencies = [ + "as-any", + "async-stream", + "base64", + "bytes", + "eventsource-stream", + "fastrand", + "futures", + "futures-timer", + "glob", + "http", + "mime", + "mime_guess", + "nanoid", + "ordered-float", + "pin-project-lite", + "reqwest", + "rig-derive", + "rmcp", + "schemars", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-futures", + "url", +] + +[[package]] +name = "rig-derive" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643eb495acbd4bd5976164cd068f186d534f741def8d5d052f860266b98d07f8" +dependencies = [ + "convert_case", + "deluxe", + "indoc", + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rmcp" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0810a9f717d9828f475fe1f629f4c305c8464b7f496c3a854b58d29e65f4058e" +dependencies = [ + "async-trait", + "base64", + "chrono", + "futures", + "pastey", + "pin-project-lite", + "rmcp-macros", + "schemars", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "rmcp-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aefac48c364756e97f04c0401ba3231e8607882c7c1d92da0437dc16307904d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "chrono", + "dyn-clone", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stackql-agent" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "futures", + "rig-core", + "rmcp", + "serde_json", + "stackql-mcp", + "tokio", +] + +[[package]] +name = "stackql-mcp" +version = "0.2.0" +dependencies = [ + "rmcp", + "serde", + "serde_json", + "sha2", + "tokio", + "ureq", + "zip", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots 0.26.11", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "futures", + "futures-task", + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" +dependencies = [ + "base64", + "cookie_store", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "ureq-proto", + "utf8-zero", + "webpki-roots 1.0.7", +] + +[[package]] +name = "ureq-proto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.7", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[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-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +dependencies = [ + "crc32fast", + "flate2", + "indexmap", + "memchr", + "typed-path", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index e159ca7..0e56e8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,52 +1,53 @@ -[workspace] -members = ["auditron", "stackql-agent"] -resolver = "3" - -[package] -name = "stackql-mcp" -version = "0.1.1" -edition = "2021" -# Effective MSRV is set by rmcp 1.x (let-chains): 1.88. -rust-version = "1.88" -description = "Embedded StackQL MCP server for Rust agentic apps - cloud queries and provisioning over SQL, served over MCP" -license = "MIT" -repository = "https://github.com/stackql/stackql-mcp-rs" -documentation = "https://docs.rs/stackql-mcp" -readme = "README.md" -keywords = ["mcp", "stackql", "sql", "cloud", "agents"] -categories = ["api-bindings", "development-tools"] -exclude = [".github/", "CLAUDE.md", "auditron/", "controls/", "pr.md"] - -[package.metadata.docs.rs] -all-features = true - -[features] -default = ["sidecar"] -# Download the platform .mcpb bundle at first run, verify it against the -# sha256 pins baked into the crate, and cache it under ~/.stackql. -sidecar = ["dep:ureq"] -# Caller embeds the .mcpb with include_bytes! and the crate extracts it on -# first run - no network at runtime. -vendored = [] - -[dependencies] -rmcp = { version = "1.7", default-features = false, features = ["client", "transport-async-rw"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -sha2 = "0.10" -tokio = { version = "1", features = ["process", "io-util", "rt"] } -ureq = { version = "3", optional = true } -zip = { version = "8", default-features = false, features = ["deflate"] } - -[dev-dependencies] -tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] } - -[[example]] -name = "minimal" - -[[example]] -name = "launcher" - -[[example]] -name = "fetch_bundle" -required-features = ["sidecar"] +[workspace] +members = ["auditron", "stackql-agent"] +resolver = "3" + +[package] +name = "stackql-mcp" +version = "0.2.0" +edition = "2021" +# Effective MSRV is set by rmcp 1.x (let-chains): 1.88. +rust-version = "1.88" +description = "Embedded StackQL MCP server for Rust agentic apps - cloud queries and provisioning over SQL, served over MCP" +license = "MIT" +repository = "https://github.com/stackql/stackql-mcp-rs" +documentation = "https://docs.rs/stackql-mcp" +readme = "README.md" +keywords = ["mcp", "stackql", "sql", "cloud", "agents"] +categories = ["api-bindings", "development-tools"] +exclude = [".github/", "CLAUDE.md", "auditron/", "controls/", "pr.md"] + +[package.metadata.docs.rs] +all-features = true + +[features] +default = ["sidecar"] +# Resolve the latest stackql release, download the platform .mcpb bundle at +# first run, verify its sha256 (release .sha256 asset, or the pins baked into +# the crate for BundleVersion::Pinned), and cache it under ~/.stackql. +sidecar = ["dep:ureq"] +# Caller embeds the .mcpb with include_bytes! and the crate extracts it on +# first run - no network at runtime. +vendored = [] + +[dependencies] +rmcp = { version = "1.7", default-features = false, features = ["client", "transport-async-rw"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +sha2 = "0.10" +tokio = { version = "1", features = ["process", "io-util", "rt"] } +ureq = { version = "3", optional = true } +zip = { version = "8", default-features = false, features = ["deflate"] } + +[dev-dependencies] +tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] } + +[[example]] +name = "minimal" + +[[example]] +name = "launcher" + +[[example]] +name = "fetch_bundle" +required-features = ["sidecar"] diff --git a/README.md b/README.md index 09d8461..67ac5dc 100644 --- a/README.md +++ b/README.md @@ -1,119 +1,120 @@ -# stackql-mcp - -[![crates.io](https://img.shields.io/crates/v/stackql-mcp.svg)](https://crates.io/crates/stackql-mcp) -[![docs.rs](https://docs.rs/stackql-mcp/badge.svg)](https://docs.rs/stackql-mcp) -[![ci](https://github.com/stackql/stackql-mcp-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/stackql/stackql-mcp-rs/actions/workflows/ci.yml) - -Embedded [StackQL](https://stackql.io) MCP server for Rust agentic apps. StackQL exposes cloud providers (AWS, GitHub, Google, Azure, and more) as SQL tables; this crate acquires the `stackql` binary, launches it as an MCP server over stdio, and hands you a connected [rmcp](https://crates.io/crates/rmcp) client. - -## Quickstart - -```rust -use stackql_mcp::{Mode, StackqlMcp}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let server = StackqlMcp::builder() - .mode(Mode::ReadOnly) - .auth(serde_json::json!({"github": {"type": "null_auth"}})) - .start() - .await?; - let tools = server.list_all_tools().await?; - println!("{} tools available", tools.len()); - server.shutdown().await?; - Ok(()) -} -``` - -Run it: `cargo run --example minimal`. The github provider in `null_auth` mode needs no cloud credentials. - -## Acquisition modes - -Two ways to get the server binary, both behind the same API: - -- sidecar (default feature): downloads the platform's `.mcpb` bundle at first run, verifies its sha256 against pins baked into the crate, and caches it. Subsequent starts are offline. -- vendored (`vendored` feature): embed the bundle in your binary and extract it on first run - no network at runtime, a single shippable binary: - -```rust -let server = StackqlMcp::builder() - .bundle_bytes(include_bytes!("../stackql-mcp-linux-x64.mcpb")) - .start() - .await?; -``` - -Bundles are published per release at [stackql/stackql](https://github.com/stackql/stackql/releases) by [stackql/stackql-mcpb-packaging](https://github.com/stackql/stackql-mcpb-packaging). Platforms: linux-x64, linux-arm64, windows-x64, darwin-universal. - -## Safety modes - -The server enforces a safety contract per session; the crate defaults to the most restrictive. - -| Mode | Allows | -|---|---| -| `Mode::ReadOnly` (default) | SELECT and metadata tools only | -| `Mode::Safe` | reads plus non-destructive mutations | -| `Mode::DeleteSafe` | safe plus deletes | -| `Mode::FullAccess` | everything, including lifecycle provisioning | - -Escalation is an explicit caller opt-in via `.mode(...)`. - -## Cache and overrides - -The binary cache is shared with the StackQL npm and PyPI wrappers: `~/.stackql/mcp-server-bin///`. Existing cache entries are used before any download. - -Env overrides: - -- `STACKQL_MCP_BIN`: path to a stackql binary to run directly (skips acquisition) -- `STACKQL_MCP_BUNDLE`: path to a local `.mcpb` to extract instead of downloading - -Builder equivalents: `.binary(path)`, `.bundle_path(path)`, plus `.approot(path)` to relocate StackQL's application root (default `~/.stackql`). - -If you bring your own MCP stack, `Builder::command()` returns a `std::process::Command` preloaded with the canonical launch arguments instead of starting anything. - -## Demo app: auditron - -The repo ships `auditron`, a terminal compliance copilot built on this crate: point-in-time control checks with auditor-ready evidence packs. Control packs are YAML data under [controls/](controls/); the github pack runs unauthenticated, so it works with zero cloud credentials. - -```sh -cargo run -p auditron -- scan # live TUI, github-core pack -cargo run -p auditron -- scan --no-tui # line output for CI/pipes -cargo run -p auditron -- scan --var org=your-org # point it at your org -cargo run -p auditron -- evidence --out evidence-2026-06.zip -``` - -The TUI streams pass/fail/error per control and always shows the SQL that produced a finding. Select a finding and press `e` to have Claude explain it and draft remediation steps (needs `ANTHROPIC_API_KEY`). The evidence zip contains the run manifest, the exact pack and SQL, and per-control CSVs - re-runnable by an auditor. - -auditron is also the single-binary pitch. Build it with the server embedded: - -```sh -BUNDLE=$(cargo run -p stackql-mcp --example fetch_bundle) -STACKQL_MCP_BUNDLE_FILE=$BUNDLE cargo build -p auditron --features vendored --release -``` - -The resulting binary (~80 MB) carries the StackQL server inside and runs on a clean machine with no downloads. - -## Demo app: stackql-agent - -[stackql-agent](stackql-agent) is the agentic companion: it embeds this crate and wires the StackQL MCP tools into a [rig](https://docs.rig.rs) agent. One binary becomes a platform-engineering, SRE, or audit agent by swapping a system prompt - the backend and the read-only contract are identical across all three. - -```sh -export ANTHROPIC_API_KEY=sk-ant-... -cargo run -p stackql-agent -- --persona platform # also: sre, audit -cargo run -p stackql-agent -- --check # pre-flight, no model calls -``` - -It runs against public GitHub data with zero cloud credentials; point `--auth` at a credentialed provider for IGA, CSPM, FinOps, and AWS/Google/Azure. The integration is about ten lines: `server.list_all_tools()` plus `server.peer()` feed straight into rig's `rmcp_tools()`. - -## Development - -```sh -cargo test --workspace # unit tests -cargo test -p stackql-mcp --features vendored # vendored path -cargo test -p stackql-mcp --test conformance -- --include-ignored # downloads the pinned bundle -cargo fmt --check && cargo clippy --workspace --all-targets -- -D warnings -``` - -MSRV: 1.88 (set by rmcp 1.x). - -## License - -MIT +# stackql-mcp + +[![crates.io](https://img.shields.io/crates/v/stackql-mcp.svg)](https://crates.io/crates/stackql-mcp) +[![docs.rs](https://docs.rs/stackql-mcp/badge.svg)](https://docs.rs/stackql-mcp) +[![ci](https://github.com/stackql/stackql-mcp-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/stackql/stackql-mcp-rs/actions/workflows/ci.yml) + +Embedded [StackQL](https://stackql.io) MCP server for Rust agentic apps. StackQL exposes cloud providers (AWS, GitHub, Google, Azure, and more) as SQL tables; this crate acquires the `stackql` binary, launches it as an MCP server over stdio, and hands you a connected [rmcp](https://crates.io/crates/rmcp) client. + +## Quickstart + +```rust +use stackql_mcp::{Mode, StackqlMcp}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server = StackqlMcp::builder() + .mode(Mode::ReadOnly) + .auth(serde_json::json!({"github": {"type": "null_auth"}})) + .start() + .await?; + let tools = server.list_all_tools().await?; + println!("{} tools available", tools.len()); + server.shutdown().await?; + Ok(()) +} +``` + +Run it: `cargo run --example minimal`. The github provider in `null_auth` mode needs no cloud credentials. + +## Acquisition modes + +Two ways to get the server binary, both behind the same API: + +- sidecar (default feature): resolves the latest stackql release at start-up, downloads the platform's `.mcpb` bundle on first use of that release, verifies its sha256 against the release's published `.sha256` asset, and caches it. Pin a release with `.version(BundleVersion::Pinned)` (the release whose pins are baked into the crate, verified offline) or `.version(BundleVersion::Exact("0.10.601".into()))`; `STACKQL_MCP_VERSION=latest|pinned|` overrides. If the latest release cannot be resolved (offline), the newest cached release is used, then the pinned one. +- vendored (`vendored` feature): embed the bundle in your binary and extract it on first run - no network at runtime, a single shippable binary: + +```rust +let server = StackqlMcp::builder() + .bundle_bytes(include_bytes!("../stackql-mcp-linux-x64.mcpb")) + .start() + .await?; +``` + +Bundles are published per release at [stackql/stackql](https://github.com/stackql/stackql/releases) by [stackql/stackql-mcpb-packaging](https://github.com/stackql/stackql-mcpb-packaging). Platforms: linux-x64, linux-arm64, windows-x64, darwin-universal. + +## Safety modes + +The server enforces a safety contract per session; the crate defaults to the most restrictive. + +| Mode | Allows | +|---|---| +| `Mode::ReadOnly` (default) | SELECT and metadata tools only | +| `Mode::Safe` | reads plus non-destructive mutations | +| `Mode::DeleteSafe` | safe plus deletes | +| `Mode::FullAccess` | everything, including lifecycle provisioning | + +Escalation is an explicit caller opt-in via `.mode(...)`. + +## Cache and overrides + +The binary cache is shared with the StackQL npm and PyPI wrappers: `~/.stackql/mcp-server-bin///`. Existing cache entries are used before any download. + +Env overrides: + +- `STACKQL_MCP_BIN`: path to a stackql binary to run directly (skips acquisition) +- `STACKQL_MCP_BUNDLE`: path to a local `.mcpb` to extract instead of downloading +- `STACKQL_MCP_VERSION`: `latest` (default), `pinned`, or a release version such as `0.10.601` + +Builder equivalents: `.binary(path)`, `.bundle_path(path)`, `.version(BundleVersion)`, plus `.approot(path)` to relocate StackQL's application root (default `~/.stackql`). + +If you bring your own MCP stack, `Builder::command()` returns a `std::process::Command` preloaded with the canonical launch arguments instead of starting anything. + +## Demo app: auditron + +The repo ships `auditron`, a terminal compliance copilot built on this crate: point-in-time control checks with auditor-ready evidence packs. Control packs are YAML data under [controls/](controls/); the github pack runs unauthenticated, so it works with zero cloud credentials. + +```sh +cargo run -p auditron -- scan # live TUI, github-core pack +cargo run -p auditron -- scan --no-tui # line output for CI/pipes +cargo run -p auditron -- scan --var org=your-org # point it at your org +cargo run -p auditron -- evidence --out evidence-2026-06.zip +``` + +The TUI streams pass/fail/error per control and always shows the SQL that produced a finding. Select a finding and press `e` to have Claude explain it and draft remediation steps (needs `ANTHROPIC_API_KEY`). The evidence zip contains the run manifest, the exact pack and SQL, and per-control CSVs - re-runnable by an auditor. + +auditron is also the single-binary pitch. Build it with the server embedded: + +```sh +BUNDLE=$(cargo run -p stackql-mcp --example fetch_bundle) +STACKQL_MCP_BUNDLE_FILE=$BUNDLE cargo build -p auditron --features vendored --release +``` + +The resulting binary (~80 MB) carries the StackQL server inside and runs on a clean machine with no downloads. + +## Demo app: stackql-agent + +[stackql-agent](stackql-agent) is the agentic companion: it embeds this crate and wires the StackQL MCP tools into a [rig](https://docs.rig.rs) agent. One binary becomes a platform-engineering, SRE, or audit agent by swapping a system prompt - the backend and the read-only contract are identical across all three. + +```sh +export ANTHROPIC_API_KEY=sk-ant-... +cargo run -p stackql-agent -- --persona platform # also: sre, audit +cargo run -p stackql-agent -- --check # pre-flight, no model calls +``` + +It runs against public GitHub data with zero cloud credentials; point `--auth` at a credentialed provider for IGA, CSPM, FinOps, and AWS/Google/Azure. The integration is about ten lines: `server.list_all_tools()` plus `server.peer()` feed straight into rig's `rmcp_tools()`. + +## Development + +```sh +cargo test --workspace # unit tests +cargo test -p stackql-mcp --features vendored # vendored path +cargo test -p stackql-mcp --test conformance -- --include-ignored # downloads the pinned bundle +cargo fmt --check && cargo clippy --workspace --all-targets -- -D warnings +``` + +MSRV: 1.88 (set by rmcp 1.x). + +## License + +MIT diff --git a/examples/fetch_bundle.rs b/examples/fetch_bundle.rs index 8eb2824..93b2086 100644 --- a/examples/fetch_bundle.rs +++ b/examples/fetch_bundle.rs @@ -1,4 +1,4 @@ -//! Fetch the pinned platform .mcpb into the shared cache and print its path. +//! Fetch the platform .mcpb (latest release by default) into the shared cache and print its path. //! The producer step for vendored builds: //! //! ```text diff --git a/src/acquire.rs b/src/acquire.rs index 7b9de33..9c45f00 100644 --- a/src/acquire.rs +++ b/src/acquire.rs @@ -1,129 +1,200 @@ -//! Binary acquisition: resolve a runnable stackql binary from (in order) -//! env overrides, builder overrides, vendored bytes, or sidecar download. - -use std::fs; -use std::path::{Path, PathBuf}; - -use crate::bundle; -use crate::cache; -use crate::error::{Error, Result}; -#[cfg(feature = "sidecar")] -use crate::platform::Platform; - -/// Acquisition inputs collected from the builder. Env overrides are read at -/// resolve time and take precedence over everything here. -#[derive(Default)] -pub struct Acquisition { - /// Run this binary directly, skipping bundles entirely. - pub binary: Option, - /// Extract this local .mcpb instead of downloading. - pub bundle_path: Option, - /// Embedded .mcpb bytes (vendored feature). - #[cfg(feature = "vendored")] - pub bundle_bytes: Option<&'static [u8]>, -} - -/// Resolve the server binary, acquiring it if needed. Blocking; call from a -/// blocking context (`start()` wraps this in `spawn_blocking`). -pub fn resolve_binary(acq: &Acquisition) -> Result { - // 1. Env binary override: run it as-is. - if let Some(bin) = std::env::var_os(cache::ENV_BIN).filter(|v| !v.is_empty()) { - return existing(PathBuf::from(bin), cache::ENV_BIN); - } - // 2. Builder binary override. - if let Some(bin) = &acq.binary { - return existing(bin.clone(), "Builder::binary"); - } - // 3. Env bundle override: extract a local .mcpb. No pin check - the - // override is explicit operator intent and may be a custom build. - if let Some(bundle_path) = std::env::var_os(cache::ENV_BUNDLE).filter(|v| !v.is_empty()) { - return extract_local_bundle(&PathBuf::from(bundle_path), cache::ENV_BUNDLE); - } - // 4. Builder bundle override. - if let Some(bundle_path) = &acq.bundle_path { - return extract_local_bundle(bundle_path, "Builder::bundle_path"); - } - // 5. Vendored bytes embedded by the caller. - #[cfg(feature = "vendored")] - if let Some(bytes) = acq.bundle_bytes { - return extract_vendored(bytes); - } - // 6. Sidecar: shared cache, then verified download. - #[cfg(feature = "sidecar")] - { - return sidecar(); - } - #[allow(unreachable_code)] - Err(Error::Bundle( - "no binary source: enable the sidecar feature, embed a bundle with the vendored \ - feature, or set STACKQL_MCP_BIN / STACKQL_MCP_BUNDLE" - .into(), - )) -} - -fn existing(path: PathBuf, what: &'static str) -> Result { - if path.is_file() { - Ok(path) - } else { - Err(Error::OverrideNotFound { what, path }) - } -} - -/// Extract a caller-supplied .mcpb into a cache slot keyed by its content -/// hash, so different bundles never collide. -fn extract_local_bundle(bundle_path: &Path, what: &'static str) -> Result { - if !bundle_path.is_file() { - return Err(Error::OverrideNotFound { - what, - path: bundle_path.to_path_buf(), - }); - } - let digest = crate::download::sha256_file(bundle_path)?; - let dest = cache::bin_cache_root()?.join("custom").join(&digest[..16]); - if let Some(binary) = bundle::cached_binary(&dest) { - return Ok(binary); - } - let file = fs::File::open(bundle_path)?; - bundle::extract_bundle(file, &dest) -} - -#[cfg(feature = "vendored")] -fn extract_vendored(bytes: &'static [u8]) -> Result { - use sha2::{Digest, Sha256}; - let digest = Sha256::digest(bytes); - let mut key = String::with_capacity(16); - for b in &digest[..8] { - key.push_str(&format!("{b:02x}")); - } - let dest = cache::bin_cache_root()?.join("vendored").join(key); - if let Some(binary) = bundle::cached_binary(&dest) { - return Ok(binary); - } - bundle::extract_bundle(std::io::Cursor::new(bytes), &dest) -} - -#[cfg(feature = "sidecar")] -fn sidecar() -> Result { - let platform = Platform::detect()?; - let pin = crate::pins::pin_for(platform)?; - let dest = cache::bundle_cache_dir(crate::pins::STACKQL_VERSION, platform.key())?; - if let Some(binary) = bundle::cached_binary(&dest) { - return Ok(binary); - } - - let url = crate::pins::bundle_url(pin); - let mcpb = cache::bin_cache_root()? - .join(crate::pins::STACKQL_VERSION) - .join(pin.bundle_name); - eprintln!( - "stackql-mcp: downloading {} (first run, cached at {})", - url, - dest.display() - ); - crate::download::download_verified(&url, pin.sha256, &mcpb)?; - let file = fs::File::open(&mcpb)?; - let binary = bundle::extract_bundle(file, &dest)?; - // The extracted dir is the cache; drop the archive to halve disk use. - let _ = fs::remove_file(&mcpb); - Ok(binary) -} +//! Binary acquisition: resolve a runnable stackql binary from (in order) +//! env overrides, builder overrides, vendored bytes, or sidecar download. + +use std::fs; +use std::path::{Path, PathBuf}; + +use crate::bundle; +use crate::cache; +use crate::error::{Error, Result}; +#[cfg(feature = "sidecar")] +use crate::platform::Platform; +use crate::release::BundleVersion; + +/// Acquisition inputs collected from the builder. Env overrides are read at +/// resolve time and take precedence over everything here. +#[derive(Default)] +pub struct Acquisition { + /// Run this binary directly, skipping bundles entirely. + pub binary: Option, + /// Extract this local .mcpb instead of downloading. + pub bundle_path: Option, + /// Embedded .mcpb bytes (vendored feature). + #[cfg(feature = "vendored")] + pub bundle_bytes: Option<&'static [u8]>, + /// Which release the sidecar runs (`STACKQL_MCP_VERSION` overrides). + pub version: BundleVersion, +} + +/// Resolve the server binary, acquiring it if needed. Blocking; call from a +/// blocking context (`start()` wraps this in `spawn_blocking`). +pub fn resolve_binary(acq: &Acquisition) -> Result { + // 1. Env binary override: run it as-is. + if let Some(bin) = std::env::var_os(cache::ENV_BIN).filter(|v| !v.is_empty()) { + return existing(PathBuf::from(bin), cache::ENV_BIN); + } + // 2. Builder binary override. + if let Some(bin) = &acq.binary { + return existing(bin.clone(), "Builder::binary"); + } + // 3. Env bundle override: extract a local .mcpb. No pin check - the + // override is explicit operator intent and may be a custom build. + if let Some(bundle_path) = std::env::var_os(cache::ENV_BUNDLE).filter(|v| !v.is_empty()) { + return extract_local_bundle(&PathBuf::from(bundle_path), cache::ENV_BUNDLE); + } + // 4. Builder bundle override. + if let Some(bundle_path) = &acq.bundle_path { + return extract_local_bundle(bundle_path, "Builder::bundle_path"); + } + // 5. Vendored bytes embedded by the caller. + #[cfg(feature = "vendored")] + if let Some(bytes) = acq.bundle_bytes { + return extract_vendored(bytes); + } + // 6. Sidecar: shared cache, then verified download. + #[cfg(feature = "sidecar")] + { + return sidecar(&acq.version.resolved_from_env()); + } + #[cfg(not(feature = "sidecar"))] + let _ = &acq.version; + #[allow(unreachable_code)] + Err(Error::Bundle( + "no binary source: enable the sidecar feature, embed a bundle with the vendored \ + feature, or set STACKQL_MCP_BIN / STACKQL_MCP_BUNDLE" + .into(), + )) +} + +fn existing(path: PathBuf, what: &'static str) -> Result { + if path.is_file() { + Ok(path) + } else { + Err(Error::OverrideNotFound { what, path }) + } +} + +/// Extract a caller-supplied .mcpb into a cache slot keyed by its content +/// hash, so different bundles never collide. +fn extract_local_bundle(bundle_path: &Path, what: &'static str) -> Result { + if !bundle_path.is_file() { + return Err(Error::OverrideNotFound { + what, + path: bundle_path.to_path_buf(), + }); + } + let digest = crate::download::sha256_file(bundle_path)?; + let dest = cache::bin_cache_root()?.join("custom").join(&digest[..16]); + if let Some(binary) = bundle::cached_binary(&dest) { + return Ok(binary); + } + let file = fs::File::open(bundle_path)?; + bundle::extract_bundle(file, &dest) +} + +#[cfg(feature = "vendored")] +fn extract_vendored(bytes: &'static [u8]) -> Result { + use sha2::{Digest, Sha256}; + let digest = Sha256::digest(bytes); + let mut key = String::with_capacity(16); + for b in &digest[..8] { + key.push_str(&format!("{b:02x}")); + } + let dest = cache::bin_cache_root()?.join("vendored").join(key); + if let Some(binary) = bundle::cached_binary(&dest) { + return Ok(binary); + } + bundle::extract_bundle(std::io::Cursor::new(bytes), &dest) +} + +#[cfg(feature = "sidecar")] +fn sidecar(version: &BundleVersion) -> Result { + use crate::release::{self, ResolvedBundle}; + + let platform = Platform::detect()?; + let bundle: ResolvedBundle = match release::resolve(version, platform) { + Ok(b) => b, + // Latest could not be resolved (offline, GitHub unreachable): run + // what is already on disk rather than failing the start. + Err(e) if *version == BundleVersion::Latest => { + if let Some((cached_version, binary)) = newest_cached(platform)? { + eprintln!( + "stackql-mcp: could not resolve the latest release ({e}); using cached {cached_version}" + ); + return Ok(binary); + } + eprintln!( + "stackql-mcp: could not resolve the latest release ({e}); using pinned {}", + crate::pins::STACKQL_VERSION + ); + release::pinned(platform)? + } + Err(e) => return Err(e), + }; + + let dest = cache::bundle_cache_dir(&bundle.version, platform.key())?; + if let Some(binary) = bundle::cached_binary(&dest) { + return Ok(binary); + } + + let url = bundle.url(); + let mcpb = cache::bin_cache_root()? + .join(&bundle.version) + .join(bundle.bundle_name); + eprintln!( + "stackql-mcp: downloading {} (first run, cached at {})", + url, + dest.display() + ); + crate::download::download_verified(&url, &bundle.sha256, &mcpb)?; + let file = fs::File::open(&mcpb)?; + let binary = bundle::extract_bundle(file, &dest)?; + // The extracted dir is the cache; drop the archive to halve disk use. + let _ = fs::remove_file(&mcpb); + Ok(binary) +} + +/// The highest-versioned release already extracted for `platform` in the +/// shared cache, if any: `(version, binary path)`. +#[cfg(feature = "sidecar")] +fn newest_cached(platform: Platform) -> Result> { + let root = cache::bin_cache_root()?; + let Ok(entries) = fs::read_dir(&root) else { + return Ok(None); + }; + let mut found: Vec<(Vec, String, PathBuf)> = entries + .filter_map(|e| e.ok()) + .filter_map(|e| { + let name = e.file_name().into_string().ok()?; + let key = version_key(&name)?; + let binary = bundle::cached_binary(&e.path().join(platform.key()))?; + Some((key, name, binary)) + }) + .collect(); + found.sort(); + Ok(found.pop().map(|(_, v, b)| (v, b))) +} + +/// `"0.10.601"` -> `[0, 10, 601]`; `None` for non-version dir names such as +/// `custom` or `vendored`. +#[cfg(feature = "sidecar")] +fn version_key(name: &str) -> Option> { + let parts: Vec = name + .split('.') + .map(|p| p.parse().ok()) + .collect::>()?; + (!parts.is_empty()).then_some(parts) +} + +#[cfg(all(test, feature = "sidecar"))] +mod tests { + use super::*; + + #[test] + fn version_key_orders_numerically_and_skips_non_versions() { + assert!(version_key("0.10.601") > version_key("0.10.99")); + assert_eq!(version_key("custom"), None); + assert_eq!(version_key("vendored"), None); + assert_eq!(version_key(""), None); + } +} diff --git a/src/cache.rs b/src/cache.rs index d6dfb23..18fda96 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -38,6 +38,7 @@ pub fn bin_cache_root() -> Result { /// Cache directory for one extracted bundle: /// `/.stackql/mcp-server-bin///`. +#[cfg_attr(not(feature = "sidecar"), allow(dead_code))] pub fn bundle_cache_dir(version: &str, platform_key: &str) -> Result { Ok(bin_cache_root()?.join(version).join(platform_key)) } diff --git a/src/error.rs b/src/error.rs index ba0441e..7342774 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,86 +1,87 @@ -use std::fmt; -use std::path::PathBuf; - -/// Errors produced while acquiring, verifying, or running the embedded server. -#[derive(Debug)] -#[non_exhaustive] -pub enum Error { - /// No .mcpb bundle is published for the host OS/arch. - UnsupportedPlatform { - os: &'static str, - arch: &'static str, - }, - /// The home directory could not be resolved (HOME / USERPROFILE unset). - NoHomeDir, - /// Downloading the bundle failed. - Http { url: String, message: String }, - /// The downloaded bundle did not match the sha256 pin baked into the crate. - ChecksumMismatch { - bundle: String, - expected: String, - actual: String, - }, - /// The .mcpb bundle is malformed (bad zip, missing or invalid manifest). - Bundle(String), - /// Filesystem error. - Io(std::io::Error), - /// The server process could not be spawned. - Spawn(std::io::Error), - /// MCP handshake or client error. - Mcp(String), - /// A path given via an env override or builder option does not exist. - OverrideNotFound { what: &'static str, path: PathBuf }, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::UnsupportedPlatform { os, arch } => { - write!(f, "no stackql mcp bundle is published for {os}/{arch}") - } - Error::NoHomeDir => { - write!( - f, - "could not resolve the home directory (HOME / USERPROFILE unset)" - ) - } - Error::Http { url, message } => write!(f, "download of {url} failed: {message}"), - Error::ChecksumMismatch { - bundle, - expected, - actual, - } => write!( - f, - "sha256 mismatch for {bundle}: expected {expected}, got {actual}" - ), - Error::Bundle(msg) => write!(f, "invalid .mcpb bundle: {msg}"), - Error::Io(e) => write!(f, "io error: {e}"), - Error::Spawn(e) => write!(f, "failed to spawn the stackql mcp server: {e}"), - Error::Mcp(msg) => write!(f, "mcp client error: {msg}"), - Error::OverrideNotFound { what, path } => { - write!( - f, - "{what} points to {} which does not exist", - path.display() - ) - } - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::Io(e) | Error::Spawn(e) => Some(e), - _ => None, - } - } -} - -impl From for Error { - fn from(e: std::io::Error) -> Self { - Error::Io(e) - } -} - -pub type Result = std::result::Result; +use std::fmt; +use std::path::PathBuf; + +/// Errors produced while acquiring, verifying, or running the embedded server. +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + /// No .mcpb bundle is published for the host OS/arch. + UnsupportedPlatform { + os: &'static str, + arch: &'static str, + }, + /// The home directory could not be resolved (HOME / USERPROFILE unset). + NoHomeDir, + /// Downloading the bundle failed. + Http { url: String, message: String }, + /// The downloaded bundle did not match its expected sha256 (the pin baked + /// into the crate, or the release's published .sha256 asset). + ChecksumMismatch { + bundle: String, + expected: String, + actual: String, + }, + /// The .mcpb bundle is malformed (bad zip, missing or invalid manifest). + Bundle(String), + /// Filesystem error. + Io(std::io::Error), + /// The server process could not be spawned. + Spawn(std::io::Error), + /// MCP handshake or client error. + Mcp(String), + /// A path given via an env override or builder option does not exist. + OverrideNotFound { what: &'static str, path: PathBuf }, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::UnsupportedPlatform { os, arch } => { + write!(f, "no stackql mcp bundle is published for {os}/{arch}") + } + Error::NoHomeDir => { + write!( + f, + "could not resolve the home directory (HOME / USERPROFILE unset)" + ) + } + Error::Http { url, message } => write!(f, "download of {url} failed: {message}"), + Error::ChecksumMismatch { + bundle, + expected, + actual, + } => write!( + f, + "sha256 mismatch for {bundle}: expected {expected}, got {actual}" + ), + Error::Bundle(msg) => write!(f, "invalid .mcpb bundle: {msg}"), + Error::Io(e) => write!(f, "io error: {e}"), + Error::Spawn(e) => write!(f, "failed to spawn the stackql mcp server: {e}"), + Error::Mcp(msg) => write!(f, "mcp client error: {msg}"), + Error::OverrideNotFound { what, path } => { + write!( + f, + "{what} points to {} which does not exist", + path.display() + ) + } + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::Io(e) | Error::Spawn(e) => Some(e), + _ => None, + } + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::Io(e) + } +} + +pub type Result = std::result::Result; diff --git a/src/lib.rs b/src/lib.rs index d2fb26e..d5a7064 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,290 +1,314 @@ -//! Embedded StackQL MCP server for Rust agentic apps. -//! -//! StackQL exposes cloud providers (AWS, GitHub, Google, Azure, ...) as SQL -//! tables, served over the Model Context Protocol. This crate acquires the -//! `stackql` binary, launches it as an MCP server over stdio, and hands you a -//! connected [`rmcp`] client. -//! -//! Two acquisition modes behind one API: -//! -//! - sidecar (default feature): download the platform's .mcpb bundle at first -//! run, verify its sha256 against pins baked into the crate, and cache it -//! under `~/.stackql/mcp-server-bin/` (shared with the npm and PyPI -//! wrappers) -//! - vendored (`vendored` feature): embed the .mcpb with `include_bytes!` and -//! extract on first run - no network at runtime, single shippable binary -//! -//! ```no_run -//! use stackql_mcp::{Mode, StackqlMcp}; -//! -//! # async fn run() -> Result<(), Box> { -//! let server = StackqlMcp::builder() -//! .mode(Mode::ReadOnly) -//! .auth(serde_json::json!({"github": {"type": "null_auth"}})) -//! .start() -//! .await?; -//! let tools = server.list_all_tools().await?; -//! println!("{} tools available", tools.len()); -//! server.shutdown().await?; -//! # Ok(()) -//! # } -//! ``` - -mod acquire; -mod bundle; -mod cache; -mod download; -mod error; -mod launch; -mod pins; -mod platform; - -use std::ops::Deref; -use std::path::PathBuf; -use std::process::Stdio; - -use rmcp::service::RunningService; -use rmcp::{RoleClient, ServiceExt}; - -pub use cache::{ENV_BIN, ENV_BUNDLE}; -pub use error::{Error, Result}; -pub use pins::{Pin, PINS, STACKQL_VERSION}; -pub use platform::Platform; - -/// Safety contract for query / mutation / lifecycle tools, enforced -/// server-side. Maps to `server.mode` in the server's `--mcp.config`. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum Mode { - /// SELECT and metadata tools only. The default: escalation is an - /// explicit caller opt-in. - #[default] - ReadOnly, - /// Reads plus non-destructive mutations (the server's own default). - Safe, - /// Safe plus deletes. - DeleteSafe, - /// All operations, including lifecycle provisioning. - FullAccess, -} - -impl Mode { - /// The wire value for `server.mode`. - pub fn as_str(self) -> &'static str { - match self { - Mode::ReadOnly => "read_only", - Mode::Safe => "safe", - Mode::DeleteSafe => "delete_safe", - Mode::FullAccess => "full_access", - } - } -} - -/// Download the pinned .mcpb bundle for the host platform into the shared -/// cache (verified against the baked sha256 pin) and return its path. Skips -/// the download when a verified copy is already present. -/// -/// This is the producer side of vendored builds: fetch the bundle once on the -/// build machine, then embed it with [`include_bundle!`]. -#[cfg(feature = "sidecar")] -pub fn fetch_bundle() -> Result { - let platform = Platform::detect()?; - let pin = pins::pin_for(platform)?; - let dest = cache::bin_cache_root()? - .join(pins::STACKQL_VERSION) - .join(pin.bundle_name); - if dest.is_file() && download::sha256_file(&dest)? == pin.sha256 { - return Ok(dest); - } - download::download_verified(&pins::bundle_url(pin), pin.sha256, &dest)?; - Ok(dest) -} - -/// Embed the .mcpb bundle named by the compile-time env var -/// `STACKQL_MCP_BUNDLE_FILE`, for use with `Builder::bundle_bytes` (vendored -/// feature): -/// -/// ```ignore -/// let server = StackqlMcp::builder() -/// .bundle_bytes(stackql_mcp::include_bundle!()) -/// .start() -/// .await?; -/// ``` -/// -/// Build with `STACKQL_MCP_BUNDLE_FILE=/abs/path/to/bundle.mcpb cargo build`. -/// Pair with [`fetch_bundle`] to produce the bundle. -#[macro_export] -macro_rules! include_bundle { - () => { - include_bytes!(env!( - "STACKQL_MCP_BUNDLE_FILE", - "set STACKQL_MCP_BUNDLE_FILE to the absolute path of the platform .mcpb bundle \ - (see stackql_mcp::fetch_bundle)" - )) - }; -} - -/// Entry point. See the crate docs for the full example. -pub struct StackqlMcp; - -impl StackqlMcp { - pub fn builder() -> Builder { - Builder::default() - } -} - -/// Configures and starts the embedded server. -#[derive(Default)] -pub struct Builder { - mode: Mode, - auth: Option, - approot: Option, - acquisition: acquire::Acquisition, -} - -impl Builder { - /// Safety mode for the server. Defaults to [`Mode::ReadOnly`]. - pub fn mode(mut self, mode: Mode) -> Self { - self.mode = mode; - self - } - - /// Provider auth document, passed to the server as `--auth=`. - /// Example: `json!({"github": {"type": "null_auth"}})`. - pub fn auth(mut self, auth: serde_json::Value) -> Self { - self.auth = Some(auth); - self - } - - /// Override the server's application root. Defaults to `/.stackql`. - pub fn approot(mut self, approot: impl Into) -> Self { - self.approot = Some(approot.into()); - self - } - - /// Run an existing stackql binary instead of acquiring one. The - /// `STACKQL_MCP_BIN` env var takes precedence over this. - pub fn binary(mut self, path: impl Into) -> Self { - self.acquisition.binary = Some(path.into()); - self - } - - /// Extract a local .mcpb bundle instead of downloading. The - /// `STACKQL_MCP_BUNDLE` env var takes precedence over this. - pub fn bundle_path(mut self, path: impl Into) -> Self { - self.acquisition.bundle_path = Some(path.into()); - self - } - - /// Embed the .mcpb bundle in your binary and extract it on first run: - /// `builder.bundle_bytes(include_bytes!("../stackql-mcp-linux-x64.mcpb"))`. - #[cfg(feature = "vendored")] - pub fn bundle_bytes(mut self, bytes: &'static [u8]) -> Self { - self.acquisition.bundle_bytes = Some(bytes); - self - } - - /// Resolve the binary (acquiring it if needed) and return a - /// [`std::process::Command`] preloaded with the canonical launch args. - /// Blocking. The escape hatch for callers bringing their own MCP stack - /// or process supervision; stdio configuration is left to the caller. - pub fn command(&self) -> Result { - let binary = acquire::resolve_binary(&self.acquisition)?; - let approot = self.resolved_approot()?; - let mut cmd = std::process::Command::new(binary); - cmd.args(launch::launch_args(self.mode, &approot, self.auth.as_ref())); - Ok(cmd) - } - - /// Acquire the binary if needed, spawn the server, and complete the MCP - /// handshake. Must be called from within a tokio runtime. - pub async fn start(self) -> Result { - let approot = self.resolved_approot()?; - let acquisition = self.acquisition; - let binary = tokio::task::spawn_blocking(move || acquire::resolve_binary(&acquisition)) - .await - .map_err(|e| Error::Mcp(format!("acquisition task failed: {e}")))??; - - let mut child = tokio::process::Command::new(&binary) - .args(launch::launch_args(self.mode, &approot, self.auth.as_ref())) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - // Diagnostics belong on stderr; let them flow through. - .stderr(Stdio::inherit()) - .kill_on_drop(true) - .spawn() - .map_err(Error::Spawn)?; - - let stdout = child - .stdout - .take() - .ok_or_else(|| Error::Mcp("child stdout not captured".into()))?; - let stdin = child - .stdin - .take() - .ok_or_else(|| Error::Mcp("child stdin not captured".into()))?; - - let client = () - .serve((stdout, stdin)) - .await - .map_err(|e| Error::Mcp(format!("initialize failed: {e}")))?; - - Ok(RunningServer { - child, - client, - binary, - }) - } - - fn resolved_approot(&self) -> Result { - match &self.approot { - Some(p) => Ok(p.clone()), - None => cache::default_approot(), - } - } -} - -/// A running embedded server: the child process handle plus a connected -/// rmcp client. Derefs to the client, so rmcp peer methods -/// (`list_all_tools`, `call_tool`, ...) are available directly. -pub struct RunningServer { - child: tokio::process::Child, - client: RunningService, - binary: PathBuf, -} - -impl RunningServer { - /// The connected rmcp client. - pub fn client(&self) -> &RunningService { - &self.client - } - - /// OS process id of the server, if it is still running. - pub fn pid(&self) -> Option { - self.child.id() - } - - /// Path of the stackql binary that was launched. - pub fn binary_path(&self) -> &std::path::Path { - &self.binary - } - - /// Close the MCP session and stop the server process. - pub async fn shutdown(self) -> Result<()> { - let RunningServer { - mut child, client, .. - } = self; - // Cancelling drops the transport; the server sees EOF on stdin and - // exits. The kill is a backstop for a wedged process. - let _ = client.cancel().await; - let _ = child.kill().await; - Ok(()) - } -} - -impl Deref for RunningServer { - type Target = RunningService; - - fn deref(&self) -> &Self::Target { - &self.client - } -} +//! Embedded StackQL MCP server for Rust agentic apps. +//! +//! StackQL exposes cloud providers (AWS, GitHub, Google, Azure, ...) as SQL +//! tables, served over the Model Context Protocol. This crate acquires the +//! `stackql` binary, launches it as an MCP server over stdio, and hands you a +//! connected [`rmcp`] client. +//! +//! Two acquisition modes behind one API: +//! +//! - sidecar (default feature): resolve the latest stackql release at +//! start-up, download the platform's .mcpb bundle on first use of that +//! release, verify its sha256 against the release's published `.sha256` +//! asset, and cache it under `~/.stackql/mcp-server-bin/` (shared with the +//! npm and PyPI wrappers). [`Builder::version`] / `STACKQL_MCP_VERSION` +//! select a pinned or exact release instead; when the latest release cannot +//! be resolved (offline) a cached release, then the crate's pinned release, +//! is used +//! - vendored (`vendored` feature): embed the .mcpb with `include_bytes!` and +//! extract on first run - no network at runtime, single shippable binary +//! +//! ```no_run +//! use stackql_mcp::{Mode, StackqlMcp}; +//! +//! # async fn run() -> Result<(), Box> { +//! let server = StackqlMcp::builder() +//! .mode(Mode::ReadOnly) +//! .auth(serde_json::json!({"github": {"type": "null_auth"}})) +//! .start() +//! .await?; +//! let tools = server.list_all_tools().await?; +//! println!("{} tools available", tools.len()); +//! server.shutdown().await?; +//! # Ok(()) +//! # } +//! ``` + +mod acquire; +mod bundle; +mod cache; +mod download; +mod error; +mod launch; +mod pins; +mod platform; +mod release; + +use std::ops::Deref; +use std::path::PathBuf; +use std::process::Stdio; + +use rmcp::service::RunningService; +use rmcp::{RoleClient, ServiceExt}; + +pub use cache::{ENV_BIN, ENV_BUNDLE}; +pub use error::{Error, Result}; +pub use pins::{Pin, PINS, STACKQL_VERSION}; +pub use platform::Platform; +pub use release::{BundleVersion, ENV_VERSION}; + +/// Safety contract for query / mutation / lifecycle tools, enforced +/// server-side. Maps to `server.mode` in the server's `--mcp.config`. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum Mode { + /// SELECT and metadata tools only. The default: escalation is an + /// explicit caller opt-in. + #[default] + ReadOnly, + /// Reads plus non-destructive mutations (the server's own default). + Safe, + /// Safe plus deletes. + DeleteSafe, + /// All operations, including lifecycle provisioning. + FullAccess, +} + +impl Mode { + /// The wire value for `server.mode`. + pub fn as_str(self) -> &'static str { + match self { + Mode::ReadOnly => "read_only", + Mode::Safe => "safe", + Mode::DeleteSafe => "delete_safe", + Mode::FullAccess => "full_access", + } + } +} + +/// Download the .mcpb bundle for the host platform into the shared cache +/// and return its path. Uses the default release selection (latest, or +/// `STACKQL_MCP_VERSION`); see [`fetch_bundle_with`] to choose explicitly. +/// Skips the download when a verified copy is already present. +/// +/// This is the producer side of vendored builds: fetch the bundle once on the +/// build machine, then embed it with [`include_bundle!`]. +#[cfg(feature = "sidecar")] +pub fn fetch_bundle() -> Result { + fetch_bundle_with(&BundleVersion::default().resolved_from_env()) +} + +/// [`fetch_bundle`] for a specific release selection. `Latest` and `Exact` +/// verify against the release's published `.sha256` asset; `Pinned` against +/// the pins baked into this crate. +#[cfg(feature = "sidecar")] +pub fn fetch_bundle_with(version: &BundleVersion) -> Result { + let platform = Platform::detect()?; + let bundle = release::resolve(version, platform)?; + let dest = cache::bin_cache_root()? + .join(&bundle.version) + .join(bundle.bundle_name); + if dest.is_file() && download::sha256_file(&dest)? == bundle.sha256 { + return Ok(dest); + } + download::download_verified(&bundle.url(), &bundle.sha256, &dest)?; + Ok(dest) +} + +/// Embed the .mcpb bundle named by the compile-time env var +/// `STACKQL_MCP_BUNDLE_FILE`, for use with `Builder::bundle_bytes` (vendored +/// feature): +/// +/// ```ignore +/// let server = StackqlMcp::builder() +/// .bundle_bytes(stackql_mcp::include_bundle!()) +/// .start() +/// .await?; +/// ``` +/// +/// Build with `STACKQL_MCP_BUNDLE_FILE=/abs/path/to/bundle.mcpb cargo build`. +/// Pair with [`fetch_bundle`] to produce the bundle. +#[macro_export] +macro_rules! include_bundle { + () => { + include_bytes!(env!( + "STACKQL_MCP_BUNDLE_FILE", + "set STACKQL_MCP_BUNDLE_FILE to the absolute path of the platform .mcpb bundle \ + (see stackql_mcp::fetch_bundle)" + )) + }; +} + +/// Entry point. See the crate docs for the full example. +pub struct StackqlMcp; + +impl StackqlMcp { + pub fn builder() -> Builder { + Builder::default() + } +} + +/// Configures and starts the embedded server. +#[derive(Default)] +pub struct Builder { + mode: Mode, + auth: Option, + approot: Option, + acquisition: acquire::Acquisition, +} + +impl Builder { + /// Safety mode for the server. Defaults to [`Mode::ReadOnly`]. + pub fn mode(mut self, mode: Mode) -> Self { + self.mode = mode; + self + } + + /// Provider auth document, passed to the server as `--auth=`. + /// Example: `json!({"github": {"type": "null_auth"}})`. + pub fn auth(mut self, auth: serde_json::Value) -> Self { + self.auth = Some(auth); + self + } + + /// Override the server's application root. Defaults to `/.stackql`. + pub fn approot(mut self, approot: impl Into) -> Self { + self.approot = Some(approot.into()); + self + } + + /// Run an existing stackql binary instead of acquiring one. The + /// `STACKQL_MCP_BIN` env var takes precedence over this. + pub fn binary(mut self, path: impl Into) -> Self { + self.acquisition.binary = Some(path.into()); + self + } + + /// Extract a local .mcpb bundle instead of downloading. The + /// `STACKQL_MCP_BUNDLE` env var takes precedence over this. + pub fn bundle_path(mut self, path: impl Into) -> Self { + self.acquisition.bundle_path = Some(path.into()); + self + } + + /// Which stackql release the sidecar runs. Defaults to + /// [`BundleVersion::Latest`]. The `STACKQL_MCP_VERSION` env var + /// (`latest`, `pinned`, or a version) takes precedence over this. Ignored + /// when a binary or bundle override is in effect. + pub fn version(mut self, version: BundleVersion) -> Self { + self.acquisition.version = version; + self + } + + /// Embed the .mcpb bundle in your binary and extract it on first run: + /// `builder.bundle_bytes(include_bytes!("../stackql-mcp-linux-x64.mcpb"))`. + #[cfg(feature = "vendored")] + pub fn bundle_bytes(mut self, bytes: &'static [u8]) -> Self { + self.acquisition.bundle_bytes = Some(bytes); + self + } + + /// Resolve the binary (acquiring it if needed) and return a + /// [`std::process::Command`] preloaded with the canonical launch args. + /// Blocking. The escape hatch for callers bringing their own MCP stack + /// or process supervision; stdio configuration is left to the caller. + pub fn command(&self) -> Result { + let binary = acquire::resolve_binary(&self.acquisition)?; + let approot = self.resolved_approot()?; + let mut cmd = std::process::Command::new(binary); + cmd.args(launch::launch_args(self.mode, &approot, self.auth.as_ref())); + Ok(cmd) + } + + /// Acquire the binary if needed, spawn the server, and complete the MCP + /// handshake. Must be called from within a tokio runtime. + pub async fn start(self) -> Result { + let approot = self.resolved_approot()?; + let acquisition = self.acquisition; + let binary = tokio::task::spawn_blocking(move || acquire::resolve_binary(&acquisition)) + .await + .map_err(|e| Error::Mcp(format!("acquisition task failed: {e}")))??; + + let mut child = tokio::process::Command::new(&binary) + .args(launch::launch_args(self.mode, &approot, self.auth.as_ref())) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + // Diagnostics belong on stderr; let them flow through. + .stderr(Stdio::inherit()) + .kill_on_drop(true) + .spawn() + .map_err(Error::Spawn)?; + + let stdout = child + .stdout + .take() + .ok_or_else(|| Error::Mcp("child stdout not captured".into()))?; + let stdin = child + .stdin + .take() + .ok_or_else(|| Error::Mcp("child stdin not captured".into()))?; + + let client = () + .serve((stdout, stdin)) + .await + .map_err(|e| Error::Mcp(format!("initialize failed: {e}")))?; + + Ok(RunningServer { + child, + client, + binary, + }) + } + + fn resolved_approot(&self) -> Result { + match &self.approot { + Some(p) => Ok(p.clone()), + None => cache::default_approot(), + } + } +} + +/// A running embedded server: the child process handle plus a connected +/// rmcp client. Derefs to the client, so rmcp peer methods +/// (`list_all_tools`, `call_tool`, ...) are available directly. +pub struct RunningServer { + child: tokio::process::Child, + client: RunningService, + binary: PathBuf, +} + +impl RunningServer { + /// The connected rmcp client. + pub fn client(&self) -> &RunningService { + &self.client + } + + /// OS process id of the server, if it is still running. + pub fn pid(&self) -> Option { + self.child.id() + } + + /// Path of the stackql binary that was launched. + pub fn binary_path(&self) -> &std::path::Path { + &self.binary + } + + /// Close the MCP session and stop the server process. + pub async fn shutdown(self) -> Result<()> { + let RunningServer { + mut child, client, .. + } = self; + // Cancelling drops the transport; the server sees EOF on stdin and + // exits. The kill is a backstop for a wedged process. + let _ = client.cancel().await; + let _ = child.kill().await; + Ok(()) + } +} + +impl Deref for RunningServer { + type Target = RunningService; + + fn deref(&self) -> &Self::Target { + &self.client + } +} diff --git a/src/pins.rs b/src/pins.rs index fb94737..bbd3ece 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -4,6 +4,10 @@ //! packaging repo (stackql/stackql-mcpb-packaging) targets. Update this table //! when bumping STACKQL_VERSION. Once the packaging repo publishes a //! consolidated platforms.json release asset, prefer rendering from that. +//! +//! These pins back `BundleVersion::Pinned` and the offline fallback; the +//! default (`BundleVersion::Latest`) verifies against the release's own +//! .sha256 asset instead - see `release.rs`. use crate::error::{Error, Result}; use crate::platform::Platform; @@ -45,6 +49,7 @@ pub const PINS: &[Pin] = &[ /// Look up the pin for a platform. Every `Platform` variant has a pin; a miss /// here is a crate bug, so it surfaces as `UnsupportedPlatform`. +#[cfg_attr(not(feature = "sidecar"), allow(dead_code))] pub fn pin_for(platform: Platform) -> Result<&'static Pin> { PINS.iter() .find(|p| p.platform_key == platform.key()) @@ -54,15 +59,6 @@ pub fn pin_for(platform: Platform) -> Result<&'static Pin> { }) } -/// Download URL for a pinned bundle. Bundles are attached to the matching -/// stackql/stackql release. -pub fn bundle_url(pin: &Pin) -> String { - format!( - "https://github.com/stackql/stackql/releases/download/v{STACKQL_VERSION}/{}", - pin.bundle_name - ) -} - #[cfg(test)] mod tests { use super::*; @@ -96,13 +92,4 @@ mod tests { assert_eq!(pin.sha256, pin.sha256.to_lowercase()); } } - - #[test] - fn bundle_url_points_at_the_pinned_release() { - let pin = pin_for(Platform::LinuxX64).unwrap(); - assert_eq!( - bundle_url(pin), - "https://github.com/stackql/stackql/releases/download/v0.10.601/stackql-mcp-linux-x64.mcpb" - ); - } } diff --git a/src/release.rs b/src/release.rs new file mode 100644 index 0000000..69f6bd2 --- /dev/null +++ b/src/release.rs @@ -0,0 +1,234 @@ +//! Which stackql release to run in sidecar mode, and how its bundle is +//! resolved and verified. +//! +//! - [`BundleVersion::Latest`] (the default): resolve the newest +//! stackql/stackql release at start-up and verify the download against the +//! `.mcpb.sha256` asset published alongside it +//! - [`BundleVersion::Pinned`]: the release whose sha256 pins are baked into +//! this crate ([`crate::STACKQL_VERSION`]) - fully offline verification, +//! and the automatic fallback when the latest release cannot be resolved +//! - [`BundleVersion::Exact`]: a specific release, verified like `Latest` + +#[cfg(feature = "sidecar")] +use crate::error::Result; +#[cfg(feature = "sidecar")] +use crate::platform::Platform; + +/// Env var selecting the release: `latest`, `pinned`, or a version such as +/// `0.10.601` (leading `v` accepted). Overrides [`crate::Builder::version`]. +pub const ENV_VERSION: &str = "STACKQL_MCP_VERSION"; + +#[cfg(feature = "sidecar")] +const RELEASES: &str = "https://github.com/stackql/stackql/releases"; + +/// Which stackql release the sidecar should run. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[non_exhaustive] +pub enum BundleVersion { + /// The newest published release, resolved at start-up (default). Falls + /// back to a cached release, then to [`BundleVersion::Pinned`], when the + /// release cannot be resolved (offline). + #[default] + Latest, + /// The release pinned in this crate ([`crate::STACKQL_VERSION`]), + /// verified against the baked sha256 pins - no resolution step. + Pinned, + /// A specific release, e.g. `0.10.601`. + Exact(String), +} + +impl BundleVersion { + /// Parse the env-var / string form: `latest`, `pinned`, or a version. + pub fn parse(s: &str) -> Self { + let s = s.trim(); + match s.to_ascii_lowercase().as_str() { + "" | "latest" => BundleVersion::Latest, + "pinned" => BundleVersion::Pinned, + _ => BundleVersion::Exact(s.trim_start_matches('v').to_string()), + } + } + + /// The effective selection: `STACKQL_MCP_VERSION` if set, else `self`. + pub fn resolved_from_env(&self) -> Self { + match std::env::var(ENV_VERSION) { + Ok(v) if !v.trim().is_empty() => BundleVersion::parse(&v), + _ => self.clone(), + } + } +} + +#[cfg(feature = "sidecar")] +/// A concrete bundle to download: release version, asset name, expected +/// sha256 and URL. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ResolvedBundle { + pub version: String, + pub bundle_name: &'static str, + pub sha256: String, +} + +#[cfg(feature = "sidecar")] +impl ResolvedBundle { + pub fn url(&self) -> String { + format!("{RELEASES}/download/v{}/{}", self.version, self.bundle_name) + } +} + +#[cfg(feature = "sidecar")] +/// The pinned bundle for `platform`, without any network access. +pub fn pinned(platform: Platform) -> Result { + let pin = crate::pins::pin_for(platform)?; + Ok(ResolvedBundle { + version: crate::pins::STACKQL_VERSION.to_string(), + bundle_name: pin.bundle_name, + sha256: pin.sha256.to_string(), + }) +} + +/// Resolve `version` for `platform`. `Latest` and `Exact` need the network +/// (two small requests: the release tag, then the `.sha256` asset). +#[cfg(feature = "sidecar")] +pub fn resolve(version: &BundleVersion, platform: Platform) -> Result { + let bundle_name = crate::pins::pin_for(platform)?.bundle_name; + let version = match version { + BundleVersion::Pinned => return pinned(platform), + BundleVersion::Latest => latest_version()?, + BundleVersion::Exact(v) => v.clone(), + }; + let sha256 = published_sha256(&version, bundle_name)?; + Ok(ResolvedBundle { + version, + bundle_name, + sha256, + }) +} + +/// The newest release version: `releases/latest` redirects to +/// `releases/tag/v`; read the redirect instead of following it. +#[cfg(feature = "sidecar")] +fn latest_version() -> Result { + use crate::error::Error; + let url = format!("{RELEASES}/latest"); + let http = |message: String| Error::Http { + url: url.clone(), + message, + }; + let response = ureq::get(&url) + .config() + .max_redirects(0) + .max_redirects_will_error(false) + .build() + .call() + .map_err(|e| http(e.to_string()))?; + let location = response + .headers() + .get("location") + .and_then(|v| v.to_str().ok()) + .ok_or_else(|| { + http(format!( + "expected a redirect, got HTTP {}", + response.status() + )) + })?; + parse_tag_location(location) + .map(str::to_string) + .ok_or_else(|| http(format!("unexpected redirect target {location}"))) +} + +/// Fetch and parse the `.mcpb.sha256` asset for one release/bundle. +#[cfg(feature = "sidecar")] +fn published_sha256(version: &str, bundle_name: &str) -> Result { + use crate::error::Error; + let url = format!("{RELEASES}/download/v{version}/{bundle_name}.sha256"); + let http = |message: String| Error::Http { + url: url.clone(), + message, + }; + let body = ureq::get(&url) + .call() + .map_err(|e| http(e.to_string()))? + .body_mut() + .read_to_string() + .map_err(|e| http(e.to_string()))?; + parse_sha256_asset(&body) + .map(str::to_string) + .ok_or_else(|| http(format!("malformed sha256 asset: {body:?}"))) +} + +#[cfg(feature = "sidecar")] +/// `.../releases/tag/v0.10.601` -> `0.10.601`. +fn parse_tag_location(location: &str) -> Option<&str> { + let (_, tag) = location.trim_end_matches('/').rsplit_once("/tag/")?; + let version = tag.trim_start_matches('v'); + (!version.is_empty() + && version + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-')) + .then_some(version) +} + +#[cfg(feature = "sidecar")] +/// First token of a `sha256sum`-style line, validated as 64 hex chars. +fn parse_sha256_asset(body: &str) -> Option<&str> { + let token = body.split_whitespace().next()?; + (token.len() == 64 && token.chars().all(|c| c.is_ascii_hexdigit())).then_some(token) +} + +#[cfg(test)] +mod tests { + #[allow(unused_imports)] + use super::*; + + #[test] + fn parses_version_selection() { + assert_eq!(BundleVersion::parse("latest"), BundleVersion::Latest); + assert_eq!(BundleVersion::parse(""), BundleVersion::Latest); + assert_eq!(BundleVersion::parse("Pinned"), BundleVersion::Pinned); + assert_eq!( + BundleVersion::parse("v0.10.601"), + BundleVersion::Exact("0.10.601".into()) + ); + } + + #[cfg(feature = "sidecar")] + #[test] + fn parses_release_tag_redirect() { + assert_eq!( + parse_tag_location("https://github.com/stackql/stackql/releases/tag/v0.10.601"), + Some("0.10.601") + ); + assert_eq!( + parse_tag_location("https://github.com/stackql/stackql/releases"), + None + ); + assert_eq!( + parse_tag_location("https://github.com/x/releases/tag/"), + None + ); + } + + #[cfg(feature = "sidecar")] + #[test] + fn parses_sha256_asset() { + let hex = "4a1cad1345fba1aae1f31269fd96aebed7a7825b38f6509466c1c995ce114e52"; + assert_eq!( + parse_sha256_asset(&format!("{hex} stackql-mcp-linux-x64.mcpb\n")), + Some(hex) + ); + assert_eq!(parse_sha256_asset("not a hash"), None); + assert_eq!(parse_sha256_asset(""), None); + } + + #[cfg(feature = "sidecar")] + #[test] + fn pinned_bundle_url_points_at_the_pinned_release() { + let b = pinned(Platform::LinuxX64).unwrap(); + assert_eq!( + b.url(), + format!( + "https://github.com/stackql/stackql/releases/download/v{}/stackql-mcp-linux-x64.mcpb", + crate::pins::STACKQL_VERSION + ) + ); + } +}