From 0cccf77fafdba8648d26c6c88eba687e46d64698 Mon Sep 17 00:00:00 2001 From: opencode controller Date: Sun, 28 Jun 2026 14:01:12 -0400 Subject: [PATCH] build(slice-2): workspace deps + rutster-tap-echo skeleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tokio-tungstenite 0.24, futures-util 0.3, url 2, base64 0.22 to [workspace.dependencies] (spec §8.1). - Add crates/rutster-tap-echo as 7th workspace member (spec §2). - Transition crates/rutster-tap/Cargo.toml from stub to real manifest with the deps Tasks 2-4 will need (rutster-media, tokio, tokio-tungstenite, serde, serde_json, base64, url, thiserror, tracing). - Skeleton lib.rs (compile test) + main.rs (placeholder fn main) for rutster-tap-echo; Task 5 fills in the echo logic. - Verify cargo deny check passes against the new transitive dep tree. Spec ref: docs/superpowers/specs/2026-06-28-slice-2-agent-tap-design.md §2, §8.1. --- Cargo.lock | 226 +++++++++++++++++++++++++++- Cargo.toml | 9 ++ crates/rutster-tap-echo/Cargo.toml | 15 ++ crates/rutster-tap-echo/src/lib.rs | 23 +++ crates/rutster-tap-echo/src/main.rs | 6 + crates/rutster-tap/Cargo.toml | 16 +- deny.toml | 18 +++ 7 files changed, 304 insertions(+), 9 deletions(-) create mode 100644 crates/rutster-tap-echo/Cargo.toml create mode 100644 crates/rutster-tap-echo/src/lib.rs create mode 100644 crates/rutster-tap-echo/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 34fd9a0..f4dc9de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,6 +207,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.8.3" @@ -219,12 +225,27 @@ 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 = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.12.0" @@ -412,6 +433,16 @@ 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 = "dimpl" version = "0.7.0" @@ -427,7 +458,7 @@ dependencies = [ "nom 8.0.0", "once_cell", "pkcs8", - "rand", + "rand 0.9.4", "rcgen", "sec1", "signature", @@ -512,6 +543,23 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[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" @@ -525,6 +573,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", + "futures-macro", + "futures-sink", "futures-task", "pin-project-lite", "slab", @@ -540,6 +590,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -649,6 +710,16 @@ dependencies = [ "tower-service", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "inout" version = "0.1.4" @@ -960,14 +1031,35 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[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", - "rand_core", + "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]] @@ -977,7 +1069,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "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]] @@ -1104,7 +1205,32 @@ version = "0.0.0" [[package]] name = "rutster-tap" -version = "0.0.0" +version = "0.1.0" +dependencies = [ + "base64", + "futures-util", + "rutster-media", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-tungstenite", + "tracing", + "url", +] + +[[package]] +name = "rutster-tap-echo" +version = "0.1.0" +dependencies = [ + "futures-util", + "rutster-tap", + "serde_json", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-subscriber", +] [[package]] name = "ryu" @@ -1127,7 +1253,7 @@ dependencies = [ "bytes", "crc", "log", - "rand", + "rand 0.9.4", "rustc-hash", "slab", "thiserror 2.0.18", @@ -1211,6 +1337,17 @@ dependencies = [ "serde", ] +[[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 = "sharded-slab" version = "0.1.7" @@ -1435,6 +1572,21 @@ dependencies = [ "time-core", ] +[[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" @@ -1463,6 +1615,18 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tower" version = "0.5.3" @@ -1553,24 +1717,74 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.8.6", + "sha1", + "thiserror 1.0.69", + "utf-8", +] + [[package]] name = "typenum" version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "uuid" version = "1.23.4" diff --git a/Cargo.toml b/Cargo.toml index 36512a7..b98b5e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "crates/rutster-media", "crates/rutster-signaling-sip", "crates/rutster-tap", + "crates/rutster-tap-echo", "crates/rutster-spend", ] @@ -42,3 +43,11 @@ serde_json = "1" # on the axum Router). Axum re-exports parts of tower but the integration test # uses `tower::ServiceExt` directly, so it needs to be a workspace dep. tower = { version = "0.5", features = ["util"] } +# tokio-tungstenite 0.24: WS client + server (slice-2 tap transport). +tokio-tungstenite = { version = "0.24", features = ["connect"] } +# futures-util 0.3: Sink/Stream traits for WebSocketStream. +futures-util = "0.3" +# url 2: tap URL parsing + host validation (spec §4.4). +url = "2" +# base64 0.22: PCM <-> base64 codec for the v1 wire format (spec §3). +base64 = "0.22" diff --git a/crates/rutster-tap-echo/Cargo.toml b/crates/rutster-tap-echo/Cargo.toml new file mode 100644 index 0000000..827e1e7 --- /dev/null +++ b/crates/rutster-tap-echo/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rutster-tap-echo" +version = "0.1.0" +license.workspace = true +edition.workspace = true +repository.workspace = true + +[dependencies] +rutster-tap = { path = "../rutster-tap" } # protocol types — the wire-types-reusable contract test +tokio = { workspace = true } +tokio-tungstenite = { workspace = true } +futures-util = { workspace = true } +serde_json = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } diff --git a/crates/rutster-tap-echo/src/lib.rs b/crates/rutster-tap-echo/src/lib.rs new file mode 100644 index 0000000..9ccf105 --- /dev/null +++ b/crates/rutster-tap-echo/src/lib.rs @@ -0,0 +1,23 @@ +//! # rutster-tap-echo — the Rust reference echo brain + test server (spec §2.3, §8.4) +//! +//! Dual-purpose crate: +//! - **Standalone binary** (`cargo run -p rutster-tap-echo`): binds +//! `ws://127.0.0.1:8081/echo` and echoes `audio_in` → `audio_out` per the +//! slice-2 protocol (spec §3). The dev-loop brain the core dials out to. +//! - **In-process `EchoServer`** (Task 5 lands `EchoServer::start`): used by +//! `rutster`'s integration tests to drive the tap end-to-end without an +//! external process. +//! +//! ## Why a Rust brain at all (when the canonical brain is Python?) +//! +//! Reuses `rutster-tap`'s protocol types — **the contract test that the wire +//! types are reusable from outside the core**. Any future brain written in +//! Rust (or a step-3 OpenAI adapter in Rust) starts from this shape. The +//! Python brain (`examples/echo_brain/`) proves language-agnosticism; this +//! crate proves reusability + powers the in-process integration tests. + +#[cfg(test)] +mod tests { + #[test] + fn crate_compiles() {} +} diff --git a/crates/rutster-tap-echo/src/main.rs b/crates/rutster-tap-echo/src/main.rs new file mode 100644 index 0000000..8331b8b --- /dev/null +++ b/crates/rutster-tap-echo/src/main.rs @@ -0,0 +1,6 @@ +//! Standalone binary: bind `ws://127.0.0.1:8081/echo`, echo audio_in → audio_out. +//! Real implementation lands in Task 5; this is the skeleton that compiles. + +fn main() { + eprintln!("rutster-tap-echo: skeleton — implementation lands in Task 5"); +} diff --git a/crates/rutster-tap/Cargo.toml b/crates/rutster-tap/Cargo.toml index 2ccb1d7..576145e 100644 --- a/crates/rutster-tap/Cargo.toml +++ b/crates/rutster-tap/Cargo.toml @@ -1,8 +1,18 @@ -# crates/rutster-tap/Cargo.toml [package] name = "rutster-tap" -version = "0.0.0" +version = "0.1.0" license.workspace = true edition.workspace = true repository.workspace = true -description = "Agent audio tap — stub crate (filled in spearhead step 2)." + +[dependencies] +rutster-media = { path = "../rutster-media" } # PcmFrame (re-exported — spec §3.1) +tokio = { workspace = true } +tokio-tungstenite = { workspace = true } +futures-util = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +base64 = { workspace = true } +url = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } diff --git a/deny.toml b/deny.toml index 627aadc..20f571f 100644 --- a/deny.toml +++ b/deny.toml @@ -111,6 +111,24 @@ skip = [ # together. Both pull distinct `r-efi` versions, so the two skips # are coupled: skip one without the other and the dup remains. "getrandom@0.3.4", + # `rand` / `rand_core` / `rand_chacha` / `getrandom` v0.2-era split + # (slice-2 §8.1 task 1): `tokio-tungstenite` = "0.24" → `tungstenite` + # 0.24 pulls the OLD `rand` 0.8 chain (rand_core 0.6, rand_chacha 0.3, + # getrandom 0.2), while `str0m` (via `dimpl`/`sctp-proto`) and the + # uuid branch use the NEW `rand` 0.9 chain (rand_core 0.9, + # rand_chacha 0.9, getrandom 0.4). Both branches are fully transitive + # (we don't depend on `rand` directly). Unified only by either + # bumping `tokio-tungstenite` to a release that adopts `rand` 0.9 + # (none of the 0.24.x line does; newer 0.25+ requires features beyond + # slice-2's verbatim dep pin per spec §8.1), or forking tungstenite — + # both out of scope for Task 1. These four skips are coupled: skip + # rand@0.8.6 without its three peers and the duplicates remain. + # Revisit when `tokio-tungstenite` workspace dep moves to a release + # that unifies on `rand` 0.9. + "rand@0.8.6", + "rand_core@0.6.4", + "rand_chacha@0.3.1", + "getrandom@0.2.17", ] skip-tree = []