diff --git a/AGENTS.md b/AGENTS.md index 1c82cba..7cc1b71 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,10 +61,13 @@ - Adhere to requirements in [_CONTRIBUTING.md_](./CONTRIBUTING.md) - Always format using `cargo fmt` + - CI enforces this via `cargo fmt --check` - Always address all issues, both clippy and compiler warnings + - Verify with the same invocation CI uses: `cargo clippy --all-targets -- -D warnings --no-deps` - Do not reach for `#[allow(...)]` to bypass warnings unless it is unavoidable in the context - Be explicit when you are bypassing warnings - Always run cspell and fix spelling issues + - `npx cspell --no-progress "**"` checks all files, matching CI - If a flagged word is valid project terminology, add it to _cspell.yml_ and sort the list alphabetically ## Release process diff --git a/Cargo.lock b/Cargo.lock index 72b1d95..49636e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -307,7 +307,7 @@ dependencies = [ "objc2-foundation 0.3.2", "parking_lot", "percent-encoding", - "windows-sys 0.60.2", + "windows-sys 0.59.0", "x11rb", ] @@ -382,6 +382,18 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-compression" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3976abdc8fe7d1133d43d304afd42abdf5bc3e1319d263d223bde07b5efc4be8" +dependencies = [ + "compression-codecs", + "compression-core", + "futures-io", + "pin-project-lite", +] + [[package]] name = "async-executor" version = "1.14.0" @@ -843,6 +855,17 @@ dependencies = [ "libc", ] +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" version = "0.4.45" @@ -858,18 +881,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "301b56658598e48f3648647ac6fc887be7e7108eddfa4e9b63fcf3ec58c0cadf" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "94a65403d1a1bd28f7dc68eb8506e8874808ee5eecb59298de588e2e1407a078" dependencies = [ "anstyle", "clap_lex", @@ -933,6 +956,22 @@ dependencies = [ "memchr", ] +[[package]] +name = "compression-codecs" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" +dependencies = [ + "compression-core", + "flate2", +] + +[[package]] +name = "compression-core" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1001,6 +1040,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -1043,9 +1091,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1059,9 +1107,9 @@ checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "cxx" -version = "1.0.194" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" +checksum = "6fe442a792c7c736eea18b32a7f8a3b63cf8aafabda6760042dc2fdeda456291" dependencies = [ "cc", "cxx-build", @@ -1074,9 +1122,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.194" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" +checksum = "e3184a94384c663718698311a78a51ac00c484c10b4eeac06fb0a068c5f64fa2" dependencies = [ "cc", "codespan-reporting", @@ -1084,39 +1132,39 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.194" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" +checksum = "0148d8fd1199329ddf1d157a5e134e51ceff37c6a7ddd38615c399d81cb05d8d" dependencies = [ "clap", "codespan-reporting", "indexmap", "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] name = "cxxbridge-flags" -version = "1.0.194" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" +checksum = "52850339faed2eaadd24e286dc1d8268cc6f8a7bd9524d713adc9099566b4c89" [[package]] name = "cxxbridge-macro" -version = "1.0.194" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" +checksum = "2c77c856545d886c9bd5215409ebb63b925e262135248b50c79e5a5f194ee47c" dependencies = [ "indexmap", "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] @@ -1156,9 +1204,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] name = "device-info" @@ -1815,9 +1863,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1853,11 +1901,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] @@ -1867,10 +1913,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", + "wasm-bindgen", ] [[package]] @@ -1918,9 +1967,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "glow" @@ -2146,9 +2195,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -2156,9 +2205,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http", @@ -2166,9 +2215,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes", "futures-core", @@ -2185,9 +2234,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" dependencies = [ "atomic-waker", "bytes", @@ -2446,9 +2495,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "is_terminal_polyfill" @@ -2716,9 +2765,9 @@ dependencies = [ [[package]] name = "libwebrtc" -version = "0.3.42" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5497ff0694ddcee8f88129c78139defa5ec3fc434c3e182906ed0a72372bd9" +checksum = "e222d9f53f1fc61dd2656c4835657d987bd387019e8f6026329c918178044eff" dependencies = [ "cxx", "jni 0.21.1", @@ -2779,14 +2828,15 @@ checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "livekit" -version = "0.7.53" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa8ed793e154d63b588397ea7a84cfb3606b6d049f4d24137d9d9e0384617059" +checksum = "5c9a22daebb6058be54653f1f6a02eae6a353f9986f73b4c68dc11365134d198" dependencies = [ "base64 0.22.1", "bmrng", "bytes", "chrono", + "flate2", "futures-util", "lazy_static", "libloading", @@ -2810,19 +2860,19 @@ dependencies = [ [[package]] name = "livekit-api" -version = "0.5.6" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e827d3444235ddccf360fc1d4737034e86ee2ea2c0a696f148ecc08e7249bfd3" +checksum = "f4ed580af1187bd8fad22a981fde55cad03d4f9468d9eb9c6ecce6c753371aae" dependencies = [ "base64 0.21.7", "bytes", "device-info", "flate2", - "futures-util", "hmac", "http", "jsonwebtoken", "livekit-common", + "livekit-net", "livekit-protocol", "livekit-runtime", "log", @@ -2830,9 +2880,8 @@ dependencies = [ "parking_lot", "pbjson-types", "prost", - "rand", + "rand 0.9.4", "reqwest", - "rustls-native-certs", "scopeguard", "serde", "serde_json", @@ -2840,30 +2889,29 @@ dependencies = [ "signature", "thiserror 2.0.18", "tokio", - "tokio-rustls", - "tokio-tungstenite", "url", ] [[package]] name = "livekit-common" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a76cd816c062654d105b697ebab136da87d14126ee77b2f13280cb895bcb58" +checksum = "f903bfee33454f21c553dbf14d6f4ffbf062157b23b04fde9821cbb94ff01169" dependencies = [ "livekit-protocol", ] [[package]] name = "livekit-data-stream" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ad1cca8f05fbe29026d99931884641199df544c33777d1f1b321df6f1031dd" +checksum = "2dfdf91af5f2dade508e2f81c7f63c79f77461f3d15cabe6b7059fe5f2839d58" dependencies = [ + "async-compression", "bmrng", "bytes", "chrono", - "flate2", + "from_variants", "futures-util", "livekit-common", "livekit-protocol", @@ -2872,14 +2920,15 @@ dependencies = [ "prost", "thiserror 2.0.18", "tokio", + "tokio-stream", "uuid", ] [[package]] name = "livekit-datatrack" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201bec4becf4db2b0af616a73ff3b2421f6af3693c287b82f5182028ee36e5f5" +checksum = "376a22d6dae592bff4068a551343461b8e683ffe70aab92e037fb91cb9237ad2" dependencies = [ "anyhow", "bytes", @@ -2890,17 +2939,38 @@ dependencies = [ "livekit-protocol", "livekit-runtime", "log", - "rand", + "rand 0.9.4", "thiserror 2.0.18", "tokio", "tokio-stream", ] +[[package]] +name = "livekit-net" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46155c42ad7acccca96a0d8be2bd0f379565f93e85144fb3aa6fa406371fee22" +dependencies = [ + "async-trait", + "base64 0.21.7", + "bytes", + "futures-util", + "http", + "livekit-runtime", + "log", + "reqwest", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "tokio-tungstenite", + "url", +] + [[package]] name = "livekit-protocol" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d26880e94e2f9bab298445e7d86a3794453d211a12ddbd051bd9991a343f9ff" +checksum = "526f22bddf409e5f15449d55cf341647d7c16a1f43df9db9b05be106e4208e1c" dependencies = [ "pbjson", "pbjson-types", @@ -2918,6 +2988,19 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "livekit-token-source" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1a446c9335adaa5ccbf242fd485c7a568ff2c73c7b57fdfba0d16141f04d710" +dependencies = [ + "async-trait", + "livekit-net", + "serde", + "serde_json", + "thiserror 2.0.18", +] + [[package]] name = "lock_api" version = "0.4.14" @@ -4074,9 +4157,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", "cfg_aliases", @@ -4094,14 +4177,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.14" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "bytes", - "getrandom 0.3.4", + "getrandom 0.4.2", "lru-slab", - "rand", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash 2.1.2", "rustls", @@ -4115,16 +4199,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ "cfg_aliases", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4158,6 +4242,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -4186,6 +4281,21 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "range-alloc" version = "0.1.5" @@ -4219,7 +4329,7 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand", + "rand 0.9.4", "rand_chacha", "simd_helpers", "thiserror 2.0.18", @@ -4444,6 +4554,7 @@ dependencies = [ "image", "livekit", "livekit-api", + "livekit-token-source", "log", "parking_lot", "serde", @@ -4507,9 +4618,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "log", "once_cell", @@ -4534,9 +4645,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "web-time", "zeroize", @@ -4717,12 +4828,12 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -4733,7 +4844,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -5126,9 +5237,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -5179,9 +5290,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", @@ -5207,9 +5318,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", @@ -5348,7 +5459,7 @@ dependencies = [ "http", "httparse", "log", - "rand", + "rand 0.9.4", "rustls", "rustls-pki-types", "sha1", @@ -5810,9 +5921,9 @@ dependencies = [ [[package]] name = "webrtc-sys" -version = "0.3.39" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a555de886b8d4961e6c2878ac3ca1f716b75c77f60311c5cf6ed136d0df14e1" +checksum = "2bd328126bc3653b10eab635579f74a562a472ff032ff09b5d1681d35e6201fd" dependencies = [ "cc", "cxx", @@ -6181,15 +6292,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -6223,30 +6325,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows-threading" version = "0.2.1" @@ -6268,12 +6353,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -6286,12 +6365,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -6304,24 +6377,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -6334,12 +6395,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -6352,12 +6407,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -6370,12 +6419,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -6388,12 +6431,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winit" version = "0.30.13" diff --git a/Cargo.toml b/Cargo.toml index 117bc0f..ffb8053 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,14 +18,16 @@ tokio = { version = "1", features = ["full", "parking_lot"] } # egui-wgpu 0.35 requires wgpu ^29.0; "29.0" resolves to a compatible 29.0.x. wgpu = "29.0" winit = { version = "0.30.13", features = [ "android-native-activity" ] } -livekit = { version = "0.7.49", features = ["rustls-tls-native-roots"] } -livekit-api = { version = "0.5.4", default-features = false, features = ["access-token"] } +livekit = { version = "0.8.3", features = ["rustls-tls-native-roots"] } +livekit-api = { version = "0.6.3", default-features = false, features = ["access-token"] } +livekit-token-source = { version = "0.1.1", features = ["rustls-tls-native-roots"] } -# For local SDK development, comment out the two lines above and uncomment these -# (clone https://github.com/livekit/rust-sdks to ../rust-sdks first; see -# "Building against a local rust-sdks" in README.md): +# Building against a local rust-sdks checkout (see "Building against a local +# rust-sdks" in README.md). To build against the released crates instead, +# comment out the three lines below and uncomment the ones above: # livekit = { path = "../rust-sdks/livekit", features = ["rustls-tls-native-roots"] } # livekit-api = { path = "../rust-sdks/livekit-api", default-features = false, features = ["access-token"] } +# livekit-token-source = { path = "../rust-sdks/livekit-token-source", features = ["rustls-tls-native-roots"] } [package.metadata.bundle] name = "LiveKit Client" diff --git a/src/connect.rs b/src/connect.rs index 6a52589..aa7aa8a 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -1,26 +1,42 @@ use crate::ui::{labeled_field::LabeledTextEdit, prominent_button::ProminentButton}; +use livekit_token_source::{ + TokenSourceConfigurable, TokenSourceFetchOptions, development_token_server, +}; -/// How a room connection is authenticated. -#[derive(Clone)] +/// How a room connection is authenticated. The methods that target a known +/// server carry its URL; the token-source method learns it from the development token server. +#[derive(Clone, Debug)] pub enum Auth { /// A pre-generated access token (the room is encoded in it). - Token(String), + Token { url: String, token: String }, /// API credentials from which a join token is generated on demand. ApiKey { + url: String, api_key: String, api_secret: String, identity: String, room: String, }, + /// A LiveKit Cloud development token server, which provides both the server + /// URL and the join token. `options` customizes the request; unset + /// fields are left to server defaults. + TokenSource { + token_server_id: String, + options: TokenSourceFetchOptions, + }, } impl Auth { - /// Resolve to a room-connection JWT, generating one from the API credentials - /// when this is the API-key method. - pub fn access_token(&self) -> Result { + /// Resolve to `(server_url, token)`: the JWT is generated locally for the + /// API-key method, fetched over HTTP for the token-source method. + /// + /// Async because of that fetch — call it from the service task, not the UI + /// thread. + pub async fn connection_details(&self) -> Result<(String, String), String> { match self { - Auth::Token(token) => Ok(token.clone()), + Auth::Token { url, token } => Ok((url.clone(), token.clone())), Auth::ApiKey { + url, api_key, api_secret, identity, @@ -34,7 +50,30 @@ impl Auth { ..Default::default() }) .to_jwt() + .map(|token| (url.clone(), token)) .map_err(|e| e.to_string()), + Auth::TokenSource { + token_server_id, + options, + } => { + let token_source = development_token_server(token_server_id); + let response = token_source + .fetch(options) + .await + .map_err(|e| e.to_string())?; + Ok((response.server_url, response.participant_token)) + } + } + } + + /// Short label of the connection target for window titles: the server URL + /// when known up front, otherwise the token server id. + pub fn target_label(&self) -> &str { + match self { + Auth::Token { url, .. } | Auth::ApiKey { url, .. } => url, + Auth::TokenSource { + token_server_id, .. + } => token_server_id, } } } @@ -43,7 +82,6 @@ impl Auth { /// per application, passed to each room window as it is opened. #[derive(Clone)] pub struct ConnectSettings { - pub url: String, pub auth: Auth, pub key: String, pub auto_subscribe: bool, @@ -59,6 +97,7 @@ enum AuthMethod { #[default] ApiKey, Token, + TokenSource, } /// The root window: a welcome screen holding the only connect form in the app. @@ -74,6 +113,16 @@ pub struct ConnectView { method: AuthMethod, url: String, token: String, + token_server_id: String, + // Token-source fetch options (`ts_` to keep them apart from the API-key + // tab's identity/room). Empty means "omit, let the server default". + ts_room_name: String, + ts_participant_name: String, + ts_participant_identity: String, + ts_participant_metadata: String, + ts_agent_name: String, + ts_agent_metadata: String, + ts_agent_deployment: String, api_key: String, api_secret: String, identity: String, @@ -101,6 +150,14 @@ impl Default for ConnectView { method: AuthMethod::default(), url: env_or("LIVEKIT_URL", "ws://localhost:7880"), token: env_or("LIVEKIT_TOKEN", ""), + token_server_id: "token-server-id".to_string(), + ts_room_name: String::new(), + ts_participant_name: String::new(), + ts_participant_identity: String::new(), + ts_participant_metadata: String::new(), + ts_agent_name: String::new(), + ts_agent_metadata: String::new(), + ts_agent_deployment: String::new(), api_key: env_or("LIVEKIT_API_KEY", "devkey"), api_secret: env_or("LIVEKIT_API_SECRET", "secret"), identity: "participant-0".to_string(), @@ -116,32 +173,70 @@ impl Default for ConnectView { impl ConnectView { fn is_connect_enabled(&self) -> bool { - if self.url.trim().is_empty() { - return false; - } + // The URL only matters for the methods that use it; the token-source + // method gets its server URL from the development token server response. match self.method { AuthMethod::ApiKey => { - !self.api_key.trim().is_empty() + !self.url.trim().is_empty() + && !self.api_key.trim().is_empty() && !self.api_secret.trim().is_empty() && !self.identity.trim().is_empty() && !self.room.trim().is_empty() } - AuthMethod::Token => !self.token.trim().is_empty(), + AuthMethod::Token => !self.url.trim().is_empty() && !self.token.trim().is_empty(), + AuthMethod::TokenSource => !self.token_server_id.trim().is_empty(), } } fn current_settings(&self) -> ConnectSettings { let auth = match self.method { AuthMethod::ApiKey => Auth::ApiKey { + url: self.url.clone(), api_key: self.api_key.clone(), api_secret: self.api_secret.clone(), identity: self.identity.clone(), room: self.room.clone(), }, - AuthMethod::Token => Auth::Token(self.token.clone()), + AuthMethod::Token => Auth::Token { + url: self.url.clone(), + token: self.token.clone(), + }, + AuthMethod::TokenSource => { + // Empty (or whitespace-only) fields are left unset so the + // token server applies its defaults. + let opt = |s: &str| { + let s = s.trim(); + (!s.is_empty()).then(|| s.to_string()) + }; + let mut options = TokenSourceFetchOptions::new(); + if let Some(v) = opt(&self.ts_room_name) { + options = options.with_room_name(v); + } + if let Some(v) = opt(&self.ts_participant_name) { + options = options.with_participant_name(v); + } + if let Some(v) = opt(&self.ts_participant_identity) { + options = options.with_participant_identity(v); + } + if let Some(v) = opt(&self.ts_participant_metadata) { + options = options.with_participant_metadata(v); + } + if let Some(v) = opt(&self.ts_agent_name) { + options = options.with_agent_name(v); + } + if let Some(v) = opt(&self.ts_agent_metadata) { + options = options.with_agent_metadata(v); + } + if let Some(v) = opt(&self.ts_agent_deployment) { + options = options.with_deployment(v); + } + Auth::TokenSource { + token_server_id: self.token_server_id.clone(), + options, + } + } }; ConnectSettings { - url: self.url.clone(), auth, key: self.key.clone(), auto_subscribe: self.auto_subscribe, @@ -218,12 +313,10 @@ impl egui::Widget for ConnectForm<'_> { ui.label(egui::RichText::new("Connect to a Room").text_style(egui::TextStyle::Heading)); ui.add_space(8.0); - ui.add(LabeledTextEdit::singleline("URL", &mut view.url)); - ui.add_space(8.0); - ui.horizontal(|ui| { ui.selectable_value(&mut view.method, AuthMethod::ApiKey, "API Key"); ui.selectable_value(&mut view.method, AuthMethod::Token, "Token"); + ui.selectable_value(&mut view.method, AuthMethod::TokenSource, "TokenSource"); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { let toggle = ui .add(egui::Button::selectable(view.show_secrets, "👁")) @@ -239,15 +332,23 @@ impl egui::Widget for ConnectForm<'_> { // the eye toggle above is on. let mask = !view.show_secrets; - // Scope each method's fields under a distinct id so switching tabs - // is seen as a layout change, not an unstable widget id (egui warns - // when a rect's id changes between passes under the same parent). - ui.push_id(view.method, |ui| match view.method { + // Scope the method's fields under a *constant* id. The single-field + // tabs (Token / TokenSource) render the same full-width widget at the + // same rect, so a per-method salt would give that rect a different id + // each switch — which is exactly what egui's "rect changed id between + // passes" warning flags. A stable salt keeps the id constant. + // The URL lives inside the Token / API Key tabs (shared between + // them): the token-source method gets its URL from the development token server. + ui.push_id("auth_method_fields", |ui| match view.method { AuthMethod::Token => { + ui.add(LabeledTextEdit::singleline("URL", &mut view.url)); + ui.add_space(8.0); ui.add(LabeledTextEdit::singleline("Token", &mut view.token).password(mask)); ui.add_space(8.0); } AuthMethod::ApiKey => { + ui.add(LabeledTextEdit::singleline("URL", &mut view.url)); + ui.add_space(8.0); ui.columns(2, |columns| { columns[0].add( LabeledTextEdit::singleline("API Key", &mut view.api_key) @@ -265,6 +366,59 @@ impl egui::Widget for ConnectForm<'_> { }); ui.add_space(8.0); } + AuthMethod::TokenSource => { + ui.add(LabeledTextEdit::singleline( + "Token Server Id", + &mut view.token_server_id, + )); + ui.add_space(8.0); + + ui.label( + egui::RichText::new( + "Optional overrides — empty fields use server defaults", + ) + .text_style(egui::TextStyle::Small), + ); + ui.add_space(8.0); + ui.columns(2, |columns| { + columns[0].add(LabeledTextEdit::singleline( + "Room Name", + &mut view.ts_room_name, + )); + columns[1].add(LabeledTextEdit::singleline( + "Participant Name", + &mut view.ts_participant_name, + )); + }); + ui.add_space(8.0); + ui.columns(2, |columns| { + columns[0].add(LabeledTextEdit::singleline( + "Participant Identity", + &mut view.ts_participant_identity, + )); + columns[1].add(LabeledTextEdit::singleline( + "Participant Metadata", + &mut view.ts_participant_metadata, + )); + }); + ui.add_space(8.0); + ui.columns(2, |columns| { + columns[0].add(LabeledTextEdit::singleline( + "Agent Name", + &mut view.ts_agent_name, + )); + columns[1].add(LabeledTextEdit::singleline( + "Agent Deployment", + &mut view.ts_agent_deployment, + )); + }); + ui.add_space(8.0); + ui.add(LabeledTextEdit::singleline( + "Agent Metadata", + &mut view.ts_agent_metadata, + )); + ui.add_space(8.0); + } }); ui.add( diff --git a/src/lib.rs b/src/lib.rs index 9c027d7..1c33b69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,7 +67,7 @@ impl AppRoot { let id = self.next_window_id; self.next_window_id += 1; - let title = format!("{} - {}", APP_NAME, request.url); + let title = format!("{} - {}", APP_NAME, request.auth.target_label()); let window = RoomWindow::new( id, self.async_runtime.handle().clone(), diff --git a/src/room/window.rs b/src/room/window.rs index 075be27..3d29219 100644 --- a/src/room/window.rs +++ b/src/room/window.rs @@ -54,19 +54,10 @@ impl RoomWindow { } fn connect(&mut self) { - let token = match self.request.auth.access_token() { - Ok(token) => token, - Err(err) => { - self.connecting = false; - self.connection_failure = Some(err); - return; - } - }; self.connecting = true; self.connection_failure = None; let _ = self.service.send(AsyncCmd::RoomConnect { - url: self.request.url.clone(), - token, + auth: Box::new(self.request.auth.clone()), auto_subscribe: self.request.auto_subscribe, dynacast: self.request.dynacast, enable_e2ee: self.request.enable_e2ee, @@ -85,7 +76,7 @@ impl RoomWindow { UiCmd::ConnectResult { result } => { self.connecting = false; if let Err(err) = result { - self.connection_failure = Some(err.to_string()); + self.connection_failure = Some(err); } } UiCmd::DataTrackPublished { track } => { diff --git a/src/service.rs b/src/service.rs index addf4f9..847879c 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,3 +1,4 @@ +use crate::connect::Auth; use crate::media::{LogoTrack, MicTrack, SineParameters, SineTrack}; use livekit::{ SimulateScenario, StreamByteOptions, StreamTextOptions, @@ -12,8 +13,9 @@ use tokio::sync::mpsc::{self, error::SendError}; #[derive(Debug)] pub enum AsyncCmd { RoomConnect { - url: String, - token: String, + /// Boxed to keep `AsyncCmd` small (clippy: `result_large_err` on + /// [`LkService::send`]); the token-source options make `Auth` large. + auth: Box, auto_subscribe: bool, dynacast: bool, enable_e2ee: bool, @@ -64,7 +66,9 @@ pub enum DataStreamPayload { #[derive(Debug)] pub enum UiCmd { ConnectResult { - result: RoomResult<()>, + /// `Err` is a human-readable message: token resolution and room + /// connection can each fail, with different error types. + result: Result<(), String>, }, RoomEvent { event: RoomEvent, @@ -161,13 +165,24 @@ async fn service_task(inner: Arc, mut cmd_rx: mpsc::UnboundedRecei while let Some(event) = cmd_rx.recv().await { match event { AsyncCmd::RoomConnect { - url, - token, + auth, auto_subscribe, dynacast, enable_e2ee, key, } => { + // Resolved here rather than UI-side: the token-source method + // fetches the connection details over HTTP, which must not + // block the UI. + let (url, token) = match auth.connection_details().await { + Ok(details) => details, + Err(err) => { + log::error!("failed to resolve connection details: {err}"); + let _ = inner.ui_tx.send(UiCmd::ConnectResult { result: Err(err) }); + continue; + } + }; + log::info!("connecting to room: {}", url); let key_provider = @@ -217,7 +232,9 @@ async fn service_task(inner: Arc, mut cmd_rx: mpsc::UnboundedRecei let _ = inner.ui_tx.send(UiCmd::ConnectResult { result: Ok(()) }); } else if let Err(err) = res { log::error!("failed to connect to room: {:?}", err); - let _ = inner.ui_tx.send(UiCmd::ConnectResult { result: Err(err) }); + let _ = inner.ui_tx.send(UiCmd::ConnectResult { + result: Err(err.to_string()), + }); } } AsyncCmd::RoomDisconnect => { @@ -355,19 +372,13 @@ async fn service_task(inner: Arc, mut cmd_rx: mpsc::UnboundedRecei tokio::spawn(async move { let result = match payload { DataStreamPayload::Text(text) => { - let options = StreamTextOptions { - topic, - destination_identities, - ..Default::default() - }; + let options = StreamTextOptions::new_with_topic(topic) + .with_destination_identities(destination_identities); local.send_text(&text, options).await.map(|info| info.id) } DataStreamPayload::Bytes(bytes) => { - let options = StreamByteOptions { - topic, - destination_identities, - ..Default::default() - }; + let options = StreamByteOptions::new_with_topic(topic) + .with_destination_identities(destination_identities); local.send_bytes(bytes, options).await.map(|info| info.id) } };