diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index e079e3af0e..2333a99a50 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -699,8 +699,8 @@ "bit-set_0.5.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bit-vec\",\"req\":\"^0.6.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.3\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"bit-vec/std\"]}}", "bit-vec_0.6.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"serde_no_std\":[\"serde/alloc\"],\"serde_std\":[\"std\",\"serde/std\"],\"std\":[]}}", "bitflags_1.3.2": "{\"dependencies\":[{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.2\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.3\"}],\"features\":{\"default\":[],\"example_generated\":[],\"rustc-dep-of-std\":[\"core\",\"compiler_builtins\"]}}", - "bitflags_2.10.0": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"arbitrary\",\"req\":\"^1.0\"},{\"name\":\"bytemuck\",\"optional\":true,\"req\":\"^1.12\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.12.2\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.228\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde_lib\",\"package\":\"serde\",\"req\":\"^1.0.103\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.19\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.18\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"zerocopy\",\"req\":\"^0.8\"}],\"features\":{\"example_generated\":[],\"serde\":[\"serde_core\"],\"std\":[]}}", "bitflags_2.11.0": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"arbitrary\",\"req\":\"^1.0\"},{\"name\":\"bytemuck\",\"optional\":true,\"req\":\"^1.12\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.12.2\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.228\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde_lib\",\"package\":\"serde\",\"req\":\"^1.0.103\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.19\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.18\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"zerocopy\",\"req\":\"^0.8\"}],\"features\":{\"example_generated\":[],\"serde\":[\"serde_core\"],\"std\":[]}}", + "bitflags_2.11.1": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"arbitrary\",\"req\":\"^1.0\"},{\"name\":\"bytemuck\",\"optional\":true,\"req\":\"^1.12\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.12.2\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.228\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde_lib\",\"package\":\"serde\",\"req\":\"^1.0.103\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.19\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.18\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"zerocopy\",\"req\":\"^0.8\"}],\"features\":{\"example_generated\":[],\"serde\":[\"serde_core\"],\"std\":[]}}", "blake2_0.10.6": "{\"dependencies\":[{\"features\":[\"mac\"],\"name\":\"digest\",\"req\":\"^0.10.3\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"digest\",\"req\":\"^0.10.3\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.2.2\"}],\"features\":{\"default\":[\"std\"],\"reset\":[],\"simd\":[],\"simd_asm\":[\"simd_opt\"],\"simd_opt\":[\"simd\"],\"size_opt\":[],\"std\":[\"digest/std\"]}}", "block-buffer_0.10.4": "{\"dependencies\":[{\"name\":\"generic-array\",\"req\":\"^0.14\"}],\"features\":{}}", "block-padding_0.3.3": "{\"dependencies\":[{\"name\":\"generic-array\",\"req\":\"^0.14\"}],\"features\":{\"std\":[]}}", @@ -740,7 +740,7 @@ "chacha20_0.9.1": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"cipher\",\"req\":\"^0.4.4\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"cipher\",\"req\":\"^0.4.4\"},{\"name\":\"cpufeatures\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_arch = \\\"x86_64\\\", target_arch = \\\"x86\\\"))\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3.3\"}],\"features\":{\"std\":[\"cipher/std\"],\"zeroize\":[\"cipher/zeroize\"]}}", "chacha20poly1305_0.10.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"aead\",\"req\":\"^0.5\"},{\"default_features\":false,\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"aead\",\"req\":\"^0.5\"},{\"features\":[\"zeroize\"],\"name\":\"chacha20\",\"req\":\"^0.9\"},{\"name\":\"cipher\",\"req\":\"^0.4\"},{\"name\":\"poly1305\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"zeroize\",\"req\":\"^1.5\"}],\"features\":{\"alloc\":[\"aead/alloc\"],\"default\":[\"alloc\",\"getrandom\"],\"getrandom\":[\"aead/getrandom\",\"rand_core\"],\"heapless\":[\"aead/heapless\"],\"rand_core\":[\"aead/rand_core\"],\"reduced-round\":[],\"std\":[\"aead/std\",\"alloc\"],\"stream\":[\"aead/stream\"]}}", "chardetng_0.1.17": "{\"dependencies\":[{\"name\":\"arrayvec\",\"optional\":true,\"req\":\"^0.5.1\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"detone\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"encoding_rs\",\"req\":\"^0.8.29\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2.2.0\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.3.0\"}],\"features\":{\"multithreading\":[\"rayon\",\"arrayvec\"],\"testing-only-no-semver-guarantees-do-not-use\":[]}}", - "chrono_0.4.43": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.0\"},{\"name\":\"defmt\",\"optional\":true,\"req\":\"^1.0.1\"},{\"features\":[\"fallback\"],\"name\":\"iana-time-zone\",\"optional\":true,\"req\":\"^0.1.45\",\"target\":\"cfg(unix)\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"default_features\":false,\"name\":\"num-traits\",\"req\":\"^0.2\"},{\"name\":\"pure-rust-locales\",\"optional\":true,\"req\":\"^0.8.2\"},{\"default_features\":false,\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.7.43\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.99\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"similar-asserts\",\"req\":\"^1.6.1\"},{\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"windows-bindgen\",\"req\":\"^0.66\"},{\"name\":\"windows-link\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(windows)\"}],\"features\":{\"__internal_bench\":[],\"alloc\":[],\"clock\":[\"winapi\",\"iana-time-zone\",\"now\"],\"core-error\":[],\"default\":[\"clock\",\"std\",\"oldtime\",\"wasmbind\"],\"defmt\":[\"dep:defmt\",\"pure-rust-locales?/defmt\"],\"libc\":[],\"now\":[\"std\"],\"oldtime\":[],\"rkyv\":[\"dep:rkyv\",\"rkyv/size_32\"],\"rkyv-16\":[\"dep:rkyv\",\"rkyv?/size_16\"],\"rkyv-32\":[\"dep:rkyv\",\"rkyv?/size_32\"],\"rkyv-64\":[\"dep:rkyv\",\"rkyv?/size_64\"],\"rkyv-validation\":[\"rkyv?/validation\"],\"std\":[\"alloc\"],\"unstable-locales\":[\"pure-rust-locales\"],\"wasmbind\":[\"wasm-bindgen\",\"js-sys\"],\"winapi\":[\"windows-link\"]}}", + "chrono_0.4.44": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.0\"},{\"name\":\"defmt\",\"optional\":true,\"req\":\"^1.0.1\"},{\"features\":[\"fallback\"],\"name\":\"iana-time-zone\",\"optional\":true,\"req\":\"^0.1.45\",\"target\":\"cfg(unix)\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"default_features\":false,\"name\":\"num-traits\",\"req\":\"^0.2\"},{\"name\":\"pure-rust-locales\",\"optional\":true,\"req\":\"^0.8.2\"},{\"default_features\":false,\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.7.43\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.99\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"similar-asserts\",\"req\":\"^1.6.1\"},{\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"windows-bindgen\",\"req\":\"^0.66\"},{\"name\":\"windows-link\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(windows)\"}],\"features\":{\"__internal_bench\":[],\"alloc\":[],\"clock\":[\"winapi\",\"iana-time-zone\",\"now\"],\"core-error\":[],\"default\":[\"clock\",\"std\",\"oldtime\",\"wasmbind\"],\"defmt\":[\"dep:defmt\",\"pure-rust-locales?/defmt\"],\"libc\":[],\"now\":[\"std\"],\"oldtime\":[],\"rkyv\":[\"dep:rkyv\",\"rkyv/size_32\"],\"rkyv-16\":[\"dep:rkyv\",\"rkyv?/size_16\"],\"rkyv-32\":[\"dep:rkyv\",\"rkyv?/size_32\"],\"rkyv-64\":[\"dep:rkyv\",\"rkyv?/size_64\"],\"rkyv-validation\":[\"rkyv?/validation\"],\"std\":[\"alloc\"],\"unstable-locales\":[\"pure-rust-locales\"],\"wasmbind\":[\"wasm-bindgen\",\"js-sys\"],\"winapi\":[\"windows-link\"]}}", "chunked_transfer_1.5.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"}],\"features\":{}}", "cipher_0.4.4": "{\"dependencies\":[{\"name\":\"blobby\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"crypto-common\",\"req\":\"^0.1.6\"},{\"name\":\"inout\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.5\"}],\"features\":{\"alloc\":[],\"block-padding\":[\"inout/block-padding\"],\"dev\":[\"blobby\"],\"rand_core\":[\"crypto-common/rand_core\"],\"std\":[\"alloc\",\"crypto-common/std\",\"inout/std\"]}}", "clang-sys_1.8.1": "{\"dependencies\":[{\"name\":\"glob\",\"req\":\"^0.3\"},{\"kind\":\"build\",\"name\":\"glob\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.39\"},{\"name\":\"libloading\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\">=3.0.0, <3.7.0\"}],\"features\":{\"clang_10_0\":[\"clang_9_0\"],\"clang_11_0\":[\"clang_10_0\"],\"clang_12_0\":[\"clang_11_0\"],\"clang_13_0\":[\"clang_12_0\"],\"clang_14_0\":[\"clang_13_0\"],\"clang_15_0\":[\"clang_14_0\"],\"clang_16_0\":[\"clang_15_0\"],\"clang_17_0\":[\"clang_16_0\"],\"clang_18_0\":[\"clang_17_0\"],\"clang_3_5\":[],\"clang_3_6\":[\"clang_3_5\"],\"clang_3_7\":[\"clang_3_6\"],\"clang_3_8\":[\"clang_3_7\"],\"clang_3_9\":[\"clang_3_8\"],\"clang_4_0\":[\"clang_3_9\"],\"clang_5_0\":[\"clang_4_0\"],\"clang_6_0\":[\"clang_5_0\"],\"clang_7_0\":[\"clang_6_0\"],\"clang_8_0\":[\"clang_7_0\"],\"clang_9_0\":[\"clang_8_0\"],\"libcpp\":[],\"runtime\":[\"libloading\"],\"static\":[]}}", @@ -1421,6 +1421,7 @@ "rmcp_0.15.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"async-trait\",\"req\":\"^0.1.89\"},{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"axum\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"serde\",\"clock\",\"std\",\"oldtime\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"features\":[\"serde\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"http\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"reqwest\"],\"name\":\"oauth2\",\"optional\":true,\"req\":\"^5.0\"},{\"name\":\"pastey\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"features\":[\"tokio1\"],\"name\":\"process-wrap\",\"optional\":true,\"req\":\"^9.0\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"json\",\"stream\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"rmcp-macros\",\"optional\":true,\"req\":\"^0.15.0\"},{\"features\":[\"chrono04\"],\"name\":\"schemars\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"chrono04\"],\"kind\":\"dev\",\"name\":\"schemars\",\"req\":\"^1.1.0\"},{\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"sse-stream\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"sync\",\"macros\",\"rt\",\"time\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"name\":\"tower-service\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2.4\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"__reqwest\":[\"dep:reqwest\"],\"auth\":[\"dep:oauth2\",\"__reqwest\",\"dep:url\"],\"client\":[\"dep:tokio-stream\"],\"client-side-sse\":[\"dep:sse-stream\",\"dep:http\"],\"default\":[\"base64\",\"macros\",\"server\"],\"elicitation\":[\"dep:url\"],\"macros\":[\"dep:rmcp-macros\",\"dep:pastey\"],\"reqwest\":[\"__reqwest\",\"reqwest?/rustls-tls\"],\"reqwest-native-tls\":[\"__reqwest\",\"reqwest?/native-tls\"],\"reqwest-tls-no-provider\":[\"__reqwest\",\"reqwest?/rustls-tls-no-provider\"],\"schemars\":[\"dep:schemars\"],\"server\":[\"transport-async-rw\",\"dep:schemars\",\"dep:pastey\"],\"server-side-http\":[\"uuid\",\"dep:rand\",\"dep:tokio-stream\",\"dep:http\",\"dep:http-body\",\"dep:http-body-util\",\"dep:bytes\",\"dep:sse-stream\",\"dep:axum\",\"tower\"],\"tower\":[\"dep:tower-service\"],\"transport-async-rw\":[\"tokio/io-util\",\"tokio-util/codec\"],\"transport-child-process\":[\"transport-async-rw\",\"tokio/process\",\"dep:process-wrap\"],\"transport-io\":[\"transport-async-rw\",\"tokio/io-std\"],\"transport-streamable-http-client\":[\"client-side-sse\",\"transport-worker\"],\"transport-streamable-http-client-reqwest\":[\"transport-streamable-http-client\",\"__reqwest\"],\"transport-streamable-http-server\":[\"transport-streamable-http-server-session\",\"server-side-http\",\"transport-worker\"],\"transport-streamable-http-server-session\":[\"transport-async-rw\",\"dep:tokio-stream\"],\"transport-worker\":[\"dep:tokio-stream\"]}}", "rsa_0.9.10": "{\"dependencies\":[{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"base64ct\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"const-oid\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"alloc\",\"oid\"],\"name\":\"digest\",\"req\":\"^0.10.5\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4.1\"},{\"default_features\":false,\"features\":[\"i128\",\"prime\",\"zeroize\"],\"name\":\"num-bigint\",\"package\":\"num-bigint-dig\",\"req\":\"^0.8.6\"},{\"default_features\":false,\"name\":\"num-integer\",\"req\":\"^0.1.39\"},{\"default_features\":false,\"features\":[\"libm\"],\"name\":\"num-traits\",\"req\":\"^0.2.9\"},{\"default_features\":false,\"features\":[\"alloc\",\"pkcs8\"],\"name\":\"pkcs1\",\"req\":\"^0.7.5\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"pkcs8\",\"req\":\"^0.10.2\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand_chacha\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"rand_core\",\"req\":\"^0.6.4\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.184\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.89\"},{\"default_features\":false,\"features\":[\"oid\"],\"name\":\"sha1\",\"optional\":true,\"req\":\"^0.10.5\"},{\"default_features\":false,\"features\":[\"oid\"],\"kind\":\"dev\",\"name\":\"sha1\",\"req\":\"^0.10.5\"},{\"default_features\":false,\"features\":[\"oid\"],\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10.6\"},{\"default_features\":false,\"features\":[\"oid\"],\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10.6\"},{\"default_features\":false,\"features\":[\"oid\"],\"kind\":\"dev\",\"name\":\"sha3\",\"req\":\"^0.10.7\"},{\"default_features\":false,\"features\":[\"alloc\",\"digest\",\"rand_core\"],\"name\":\"signature\",\"req\":\">2.0, <2.3\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"spki\",\"req\":\"^0.7.3\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2.1.1\"},{\"features\":[\"alloc\"],\"name\":\"zeroize\",\"req\":\"^1.5\"}],\"features\":{\"default\":[\"std\",\"pem\",\"u64_digit\"],\"getrandom\":[\"rand_core/getrandom\"],\"hazmat\":[],\"nightly\":[\"num-bigint/nightly\"],\"pem\":[\"pkcs1/pem\",\"pkcs8/pem\"],\"pkcs5\":[\"pkcs8/encryption\"],\"serde\":[\"dep:serde\",\"num-bigint/serde\"],\"std\":[\"digest/std\",\"pkcs1/std\",\"pkcs8/std\",\"rand_core/std\",\"signature/std\"],\"u64_digit\":[\"num-bigint/u64_digit\"]}}", "rtrb_0.3.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"crossbeam-utils\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.10\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "russh-sftp_2.1.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.102\"},{\"name\":\"async-trait\",\"optional\":true,\"req\":\"^0.1.89\"},{\"features\":[\"serde\"],\"name\":\"bitflags\",\"req\":\"^2.11.1\"},{\"name\":\"bytes\",\"req\":\"^1.11.1\"},{\"name\":\"chrono\",\"req\":\"^0.4.44\"},{\"features\":[\"async_tokio\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8.2\"},{\"name\":\"dashmap\",\"req\":\"^6.1.0\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.10\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.32\"},{\"name\":\"log\",\"req\":\"^0.4.29\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.10.1\"},{\"kind\":\"dev\",\"name\":\"russh\",\"req\":\"^0.60.2\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.228\"},{\"name\":\"serde_bytes\",\"req\":\"^0.11\"},{\"name\":\"thiserror\",\"req\":\"^2.0.18\"},{\"default_features\":false,\"features\":[\"io-util\",\"rt\",\"sync\",\"time\",\"macros\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tokio-util\",\"req\":\"^0.7.18\"}],\"features\":{\"async-trait\":[\"dep:async-trait\"]}}", "rust-embed-impl_8.11.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"name\":\"rust-embed-utils\",\"req\":\"^8.11.0\"},{\"name\":\"shellexpand\",\"optional\":true,\"req\":\"^3\"},{\"default_features\":false,\"features\":[\"derive\",\"parsing\",\"proc-macro\",\"printing\"],\"name\":\"syn\",\"req\":\"^2\"},{\"name\":\"walkdir\",\"req\":\"^2.3.1\"}],\"features\":{\"compression\":[],\"debug-embed\":[],\"deterministic-timestamps\":[],\"include-exclude\":[\"rust-embed-utils/include-exclude\"],\"interpolate-folder-path\":[\"shellexpand\"],\"mime-guess\":[\"rust-embed-utils/mime-guess\"]}}", "rust-embed-utils_8.11.0": "{\"dependencies\":[{\"name\":\"globset\",\"optional\":true,\"req\":\"^0.4.8\"},{\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0.4\"},{\"name\":\"sha2\",\"req\":\"^0.10.5\"},{\"name\":\"walkdir\",\"req\":\"^2.3.1\"}],\"features\":{\"debug-embed\":[],\"include-exclude\":[\"globset\"],\"mime-guess\":[\"mime_guess\"]}}", "rust-embed_8.11.0": "{\"dependencies\":[{\"name\":\"actix-web\",\"optional\":true,\"req\":\"^4\"},{\"default_features\":false,\"features\":[\"http1\",\"tokio\"],\"name\":\"axum\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"hex\",\"optional\":true,\"req\":\"^0.4.3\"},{\"name\":\"include-flate\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0.5\"},{\"default_features\":false,\"features\":[\"server\"],\"name\":\"poem\",\"optional\":true,\"req\":\"^1.3.30\"},{\"default_features\":false,\"name\":\"rocket\",\"optional\":true,\"req\":\"^0.5.0-rc.2\"},{\"name\":\"rust-embed-impl\",\"req\":\"^8.9.0\"},{\"name\":\"rust-embed-utils\",\"req\":\"^8.9.0\"},{\"default_features\":false,\"name\":\"salvo\",\"optional\":true,\"req\":\"^0.16\"},{\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"walkdir\",\"req\":\"^2.3.2\"},{\"default_features\":false,\"name\":\"warp\",\"optional\":true,\"req\":\"^0.3\"}],\"features\":{\"actix\":[\"actix-web\",\"mime_guess\"],\"axum-ex\":[\"axum\",\"tokio\",\"mime_guess\"],\"compression\":[\"rust-embed-impl/compression\",\"include-flate\"],\"debug-embed\":[\"rust-embed-impl/debug-embed\",\"rust-embed-utils/debug-embed\"],\"deterministic-timestamps\":[\"rust-embed-impl/deterministic-timestamps\"],\"include-exclude\":[\"rust-embed-impl/include-exclude\",\"rust-embed-utils/include-exclude\"],\"interpolate-folder-path\":[\"rust-embed-impl/interpolate-folder-path\"],\"mime-guess\":[\"rust-embed-impl/mime-guess\",\"rust-embed-utils/mime-guess\"],\"poem-ex\":[\"poem\",\"tokio\",\"mime_guess\",\"hex\"],\"salvo-ex\":[\"salvo\",\"tokio\",\"mime_guess\",\"hex\"],\"warp-ex\":[\"warp\",\"tokio\",\"mime_guess\"]}}", @@ -1478,6 +1479,7 @@ "sentry-types_0.46.1": "{\"dependencies\":[{\"features\":[\"serde\"],\"name\":\"debugid\",\"req\":\"^0.8.0\"},{\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"rand\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.25.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.104\"},{\"name\":\"serde_json\",\"req\":\"^1.0.46\"},{\"name\":\"thiserror\",\"req\":\"^2.0.12\"},{\"features\":[\"formatting\",\"parsing\"],\"name\":\"time\",\"req\":\"^0.3.5\"},{\"features\":[\"serde\"],\"name\":\"url\",\"req\":\"^2.1.1\"},{\"features\":[\"serde\"],\"name\":\"uuid\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[\"protocol\"],\"protocol\":[]}}", "sentry_0.46.1": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"actix-web\",\"req\":\"^4\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.30\"},{\"name\":\"curl\",\"optional\":true,\"req\":\"^0.4.25\"},{\"name\":\"embedded-svc\",\"optional\":true,\"req\":\"^0.28.1\"},{\"name\":\"esp-idf-svc\",\"optional\":true,\"req\":\"^0.51.0\",\"target\":\"cfg(target_os = \\\"espidf\\\")\"},{\"name\":\"httpdate\",\"optional\":true,\"req\":\"^1.0.0\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.8\"},{\"name\":\"native-tls\",\"optional\":true,\"req\":\"^0.2.8\"},{\"kind\":\"dev\",\"name\":\"pretty_env_logger\",\"req\":\"^0.5.0\"},{\"default_features\":false,\"features\":[\"blocking\",\"json\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12.25\"},{\"default_features\":false,\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.18\"},{\"default_features\":false,\"name\":\"sentry-actix\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-anyhow\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-backtrace\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-contexts\",\"optional\":true,\"req\":\"^0.46.1\"},{\"features\":[\"client\"],\"name\":\"sentry-core\",\"req\":\"^0.46.1\"},{\"name\":\"sentry-debug-images\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-log\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-opentelemetry\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-panic\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-slog\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-tower\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-tracing\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.48\"},{\"kind\":\"dev\",\"name\":\"slog\",\"req\":\"^2.5.2\"},{\"features\":[\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.44\"},{\"features\":[\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.44\"},{\"features\":[\"util\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"fmt\",\"tracing-log\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"ureq\",\"optional\":true,\"req\":\"^3.0.11\"}],\"features\":{\"actix\":[\"sentry-actix\"],\"anyhow\":[\"sentry-anyhow\"],\"backtrace\":[\"sentry-backtrace\",\"sentry-tracing?/backtrace\"],\"contexts\":[\"sentry-contexts\"],\"curl\":[\"dep:curl\",\"httpdate\"],\"debug-images\":[\"sentry-debug-images\"],\"default\":[\"backtrace\",\"contexts\",\"debug-images\",\"panic\",\"transport\",\"release-health\"],\"embedded-svc-http\":[\"dep:embedded-svc\",\"dep:esp-idf-svc\"],\"log\":[\"sentry-log\"],\"logs\":[\"sentry-core/logs\",\"sentry-tracing?/logs\",\"sentry-log?/logs\"],\"native-tls\":[\"dep:native-tls\",\"reqwest?/default-tls\",\"ureq?/native-tls\"],\"opentelemetry\":[\"sentry-opentelemetry\"],\"panic\":[\"sentry-panic\"],\"release-health\":[\"sentry-core/release-health\",\"sentry-actix?/release-health\"],\"reqwest\":[\"dep:reqwest\",\"httpdate\",\"tokio\"],\"rustls\":[\"dep:rustls\",\"reqwest?/rustls-tls\",\"ureq?/rustls\"],\"slog\":[\"sentry-slog\"],\"test\":[\"sentry-core/test\"],\"tower\":[\"sentry-tower\"],\"tower-axum-matched-path\":[\"tower-http\",\"sentry-tower/axum-matched-path\"],\"tower-http\":[\"tower\",\"sentry-tower/http\"],\"tracing\":[\"sentry-tracing\"],\"transport\":[\"reqwest\",\"native-tls\"],\"ureq\":[\"dep:ureq\",\"httpdate\"]}}", "serde_1.0.228": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"result\"],\"name\":\"serde_core\",\"req\":\"=1.0.228\"},{\"name\":\"serde_derive\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[\"serde_core/alloc\"],\"default\":[\"std\"],\"derive\":[\"serde_derive\"],\"rc\":[\"serde_core/rc\"],\"std\":[\"serde_core/std\"],\"unstable\":[\"serde_core/unstable\"]}}", + "serde_bytes_0.11.19": "{\"dependencies\":[{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^2\"},{\"default_features\":false,\"name\":\"serde\",\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"serde_core\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.166\"}],\"features\":{\"alloc\":[\"serde_core/alloc\"],\"default\":[\"std\"],\"std\":[\"serde_core/std\"]}}", "serde_core_1.0.228": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"name\":\"serde_derive\",\"req\":\"=1.0.228\",\"target\":\"cfg(any())\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"}],\"features\":{\"alloc\":[],\"default\":[\"std\",\"result\"],\"rc\":[],\"result\":[],\"std\":[],\"unstable\":[]}}", "serde_derive_1.0.228": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"proc-macro\"],\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"default_features\":false,\"features\":[\"proc-macro\"],\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"clone-impls\",\"derive\",\"parsing\",\"printing\",\"proc-macro\"],\"name\":\"syn\",\"req\":\"^2.0.81\"}],\"features\":{\"default\":[],\"deserialize_in_place\":[]}}", "serde_derive_internals_0.29.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"default_features\":false,\"features\":[\"clone-impls\",\"derive\",\"parsing\",\"printing\"],\"name\":\"syn\",\"req\":\"^2.0.46\"}],\"features\":{}}", diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index c656a791a6..de07025caa 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -18,7 +18,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "bytes", "futures-core", "futures-sink", @@ -39,7 +39,7 @@ dependencies = [ "actix-rt", "actix-service", "actix-utils", - "bitflags 2.10.0", + "bitflags 2.11.1", "bytes", "bytestring", "derive_more 2.1.1", @@ -298,7 +298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" dependencies = [ "alsa-sys", - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "libc", ] @@ -1250,7 +1250,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -1287,9 +1287,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" dependencies = [ "serde_core", ] @@ -1614,9 +1614,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -1904,6 +1904,7 @@ dependencies = [ "pretty_assertions", "reqwest", "rmcp", + "russh-sftp", "serde", "serde_json", "serial_test", @@ -4399,7 +4400,7 @@ name = "crossterm" version = "0.28.1" source = "git+https://github.com/nornagon/crossterm?rev=87db8bfa6dc99427fd3b071681b07fc31c6ce995#87db8bfa6dc99427fd3b071681b07fc31c6ce995" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "crossterm_winapi", "futures-core", "mio", @@ -5045,7 +5046,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "objc2", ] @@ -6131,7 +6132,7 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "441a300bc3645a1f45cba495b9175f90f47256ce43f2ee161da0031e3ac77c92" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "bstr", "gix-path", "libc", @@ -6277,7 +6278,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b03e6cd88cc0dc1eafa1fddac0fb719e4e74b6ea58dd016e71125fde4a326bee" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "bstr", "gix-features", "gix-path", @@ -6325,7 +6326,7 @@ version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bae54ab14e4e74d5dda60b82ea7afad7c8eb3be68283d6d5f29bd2e6d47fff7" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "bstr", "filetime", "fnv", @@ -6390,7 +6391,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea064c7595eea08fdd01c70748af747d9acc40f727b61f4c8a2145a5c5fc28c" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "gix-commitgraph", "gix-date", "gix-hash", @@ -6488,7 +6489,7 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f89611f13544ca5ebeb68a502673814ef57200df60c24a61c2ce7b96f612f08b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "bstr", "gix-attributes", "gix-config-value", @@ -6571,7 +6572,7 @@ version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c08f1ec5d1e6a524f8ba291c41f0ccaef64e48ed0e8cf790b3461cae45f6d3d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "bstr", "gix-commitgraph", "gix-date", @@ -6605,7 +6606,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf82ae037de9c62850ce67beaa92ec8e3e17785ea307cdde7618edc215603b4f" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "gix-path", "libc", "windows-sys 0.61.2", @@ -6703,7 +6704,7 @@ version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "963dc2afcdb611092aa587c3f9365e749ac0a0892ff27662dbc75f26c953fbec" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "gix-commitgraph", "gix-date", "gix-hash", @@ -6806,7 +6807,7 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "futures-channel", "futures-core", "futures-executor", @@ -7688,7 +7689,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "inotify-sys", "libc", ] @@ -8119,7 +8120,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "libc", "redox_syscall 0.7.0", ] @@ -8192,7 +8193,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "libc", ] @@ -8582,7 +8583,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "jni-sys", "log", "ndk-sys", @@ -8626,7 +8627,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "cfg_aliases 0.1.1", "libc", @@ -8638,7 +8639,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -8651,7 +8652,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -8694,7 +8695,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "fsevent-sys", "inotify", "kqueue", @@ -8712,7 +8713,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", ] [[package]] @@ -8897,7 +8898,7 @@ version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "chrono", "getrandom 0.2.17", "http 1.4.0", @@ -8926,7 +8927,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "objc2", "objc2-core-graphics", "objc2-foundation", @@ -8938,7 +8939,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "objc2", "objc2-foundation", ] @@ -8959,7 +8960,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "dispatch2", "objc2", ] @@ -8970,7 +8971,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "dispatch2", "objc2", "objc2-core-foundation", @@ -9003,7 +9004,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "objc2", "objc2-core-foundation", "objc2-core-graphics", @@ -9021,7 +9022,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "block2", "libc", "objc2", @@ -9034,7 +9035,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "objc2", "objc2-core-foundation", ] @@ -9045,7 +9046,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "objc2", "objc2-core-foundation", "objc2-foundation", @@ -9057,7 +9058,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "block2", "objc2", "objc2-cloud-kit", @@ -9145,7 +9146,7 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "libc", "once_cell", "onig_sys", @@ -9173,7 +9174,7 @@ version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "foreign-types", "libc", @@ -9365,7 +9366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.45.0", + "windows-sys 0.61.2", ] [[package]] @@ -9675,7 +9676,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "crc32fast", "fdeflate", "flate2", @@ -9903,7 +9904,7 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "num-traits", "rand 0.9.3", "rand_chacha 0.9.0", @@ -10047,7 +10048,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "getopts", "memchr", "pulldown-cmark-escape", @@ -10227,7 +10228,7 @@ checksum = "453d60af031e23af2d48995e41b17023f6150044738680508b63671f8d7417dd" dependencies = [ "ahash", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.11.1", "chrono", "const_format", "csv", @@ -10558,7 +10559,7 @@ name = "ratatui" version = "0.29.0" source = "git+https://github.com/nornagon/ratatui?rev=9b2ad1298408c45918ee9f8241a6f95498cdbed2#9b2ad1298408c45918ee9f8241a6f95498cdbed2" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cassowary", "compact_str", "crossterm", @@ -10622,7 +10623,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", ] [[package]] @@ -10631,7 +10632,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", ] [[package]] @@ -10888,6 +10889,24 @@ name = "runfiles" version = "0.1.0" source = "git+https://github.com/dzbarsky/rules_rust?rev=b56cbaa8465e74127f1ea216f813cd377295ad81#b56cbaa8465e74127f1ea216f813cd377295ad81" +[[package]] +name = "russh-sftp" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09daa0ebcf53fb18d7b16167586a68b5bf2cfa3eaad49e661a19302552a2b879" +dependencies = [ + "bitflags 2.11.1", + "bytes", + "chrono", + "dashmap", + "log", + "serde", + "serde_bytes", + "thiserror 2.0.18", + "tokio", + "tokio-util", +] + [[package]] name = "rust-embed" version = "8.11.0" @@ -10974,7 +10993,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -10987,7 +11006,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -11056,7 +11075,7 @@ version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "clipboard-win", "fd-lock", @@ -11310,7 +11329,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -11323,7 +11342,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -11458,7 +11477,7 @@ version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1dd47df349a80025819f3d25c3d2f751df705d49c65a4cdc0f130f700972a48" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "sentry-backtrace", "sentry-core", "tracing-core", @@ -11492,6 +11511,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -11988,7 +12017,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.11.1", "byteorder", "bytes", "chrono", @@ -12033,7 +12062,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.11.1", "byteorder", "chrono", "crc", @@ -12424,7 +12453,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -13069,7 +13098,7 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "bytes", "futures-util", "http 1.4.0", @@ -13583,7 +13612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d97bcac5cdc5a195a4813f1855a6bc658f240452aac36caa12fd6c6f16026ab1" dependencies = [ "bindgen", - "bitflags 2.10.0", + "bitflags 2.11.1", "fslock", "gzip-header", "home", @@ -13784,7 +13813,7 @@ version = "0.31.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -13796,7 +13825,7 @@ version = "0.32.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -13808,7 +13837,7 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -14009,7 +14038,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index cf2332a276..29ae2713f0 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -334,6 +334,7 @@ regex-lite = "0.1.8" reqwest = { version = "0.12", features = ["cookies"] } rmcp = { version = "0.15.0", default-features = false } runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" } +russh-sftp = "2.1.2" rustls = { version = "0.23", default-features = false, features = [ "ring", "std", diff --git a/codex-rs/app-server/Cargo.toml b/codex-rs/app-server/Cargo.toml index c9031fd0ac..c9a2826470 100644 --- a/codex-rs/app-server/Cargo.toml +++ b/codex-rs/app-server/Cargo.toml @@ -89,6 +89,7 @@ tracing = { workspace = true, features = ["log"] } tracing-subscriber = { workspace = true, features = ["env-filter", "fmt", "json"] } url = { workspace = true } uuid = { workspace = true, features = ["serde", "v7"] } +russh-sftp = { workspace = true } [dev-dependencies] app_test_support = { workspace = true } diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index 3d7ae22ded..ae32585065 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -973,14 +973,11 @@ pub async fn run_main_with_transport_options( warn!("dropping binary payload from connection without a binary lane: {connection_id:?}"); continue; }; - match processor.process_upload_binary(connection_id, bytes).await { - Ok(responses) => { - for response in responses { - if binary_writer.send(response).await.is_err() { - break; - } - } - } + match processor + .process_upload_binary(connection_id, binary_writer, bytes) + .await + { + Ok(()) => {} Err(err) => { warn!("failed to process upload binary payload: {}", err.message); } diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 7c95a0ef3a..da0b6a9555 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -598,10 +598,11 @@ impl MessageProcessor { pub(crate) async fn process_upload_binary( &self, connection_id: ConnectionId, + binary_writer: tokio::sync::mpsc::Sender>, bytes: Vec, - ) -> Result>, JSONRPCErrorError> { + ) -> Result<(), JSONRPCErrorError> { self.fs_processor - .process_upload_sftp_bytes(connection_id, bytes) + .process_upload_sftp_bytes(connection_id, binary_writer, bytes) .await } diff --git a/codex-rs/app-server/src/request_processors/fs_processor.rs b/codex-rs/app-server/src/request_processors/fs_processor.rs index 7d672ec73a..36fc43cf10 100644 --- a/codex-rs/app-server/src/request_processors/fs_processor.rs +++ b/codex-rs/app-server/src/request_processors/fs_processor.rs @@ -2,7 +2,7 @@ use crate::error_code::internal_error; use crate::error_code::invalid_request; use crate::fs_watch::FsWatchManager; use crate::outgoing_message::ConnectionId; -use crate::request_processors::upload_sftp::UploadSftpSession; +use crate::request_processors::upload_sftp::UploadSftpLane; use base64::Engine; use base64::engine::general_purpose::STANDARD; use codex_app_server_protocol::FsCopyParams; @@ -47,7 +47,7 @@ pub(crate) struct FsRequestProcessor { fs_watch_manager: FsWatchManager, codex_home: PathBuf, upload_paths: Arc>>, - upload_sftp_sessions: Arc>>, + upload_sftp_lanes: Arc>>, } impl FsRequestProcessor { @@ -61,16 +61,13 @@ impl FsRequestProcessor { fs_watch_manager, codex_home, upload_paths: Arc::new(Mutex::new(HashSet::new())), - upload_sftp_sessions: Arc::new(Mutex::new(HashMap::new())), + upload_sftp_lanes: Arc::new(Mutex::new(HashMap::new())), } } pub(crate) async fn connection_closed(&self, connection_id: ConnectionId) { self.fs_watch_manager.connection_closed(connection_id).await; - self.upload_sftp_sessions - .lock() - .await - .remove(&connection_id); + self.upload_sftp_lanes.lock().await.remove(&connection_id); } pub(crate) async fn read_file( @@ -129,24 +126,24 @@ impl FsRequestProcessor { Ok(FsCreateUploadResponse { path: upload_path }) } - #[expect( - clippy::await_holding_invalid_type, - reason = "SFTP packets for one connection must be processed in arrival order" - )] pub(crate) async fn process_upload_sftp_bytes( &self, connection_id: ConnectionId, + binary_writer: tokio::sync::mpsc::Sender>, bytes: Vec, - ) -> Result>, JSONRPCErrorError> { - let mut sessions = self.upload_sftp_sessions.lock().await; - let session = sessions - .entry(connection_id) - .or_insert_with(|| UploadSftpSession::new(Arc::clone(&self.upload_paths))); - let result = session.process_bytes(bytes).await; - if result.is_err() { - sessions.remove(&connection_id); - } - result + ) -> Result<(), JSONRPCErrorError> { + let lane = if let Some(lane) = self.upload_sftp_lanes.lock().await.get(&connection_id) { + lane.clone() + } else { + let lane = UploadSftpLane::start(Arc::clone(&self.upload_paths), binary_writer).await; + self.upload_sftp_lanes + .lock() + .await + .entry(connection_id) + .or_insert_with(|| lane.clone()) + .clone() + }; + lane.send(bytes).await } pub(crate) async fn create_directory( diff --git a/codex-rs/app-server/src/request_processors/upload_sftp.rs b/codex-rs/app-server/src/request_processors/upload_sftp.rs index 244578acd6..cc2231572d 100644 --- a/codex-rs/app-server/src/request_processors/upload_sftp.rs +++ b/codex-rs/app-server/src/request_processors/upload_sftp.rs @@ -1,6 +1,13 @@ use crate::error_code::internal_error; -use crate::error_code::invalid_request; +use crate::transport::CHANNEL_CAPACITY; use codex_app_server_protocol::JSONRPCErrorError; +use russh_sftp::protocol::Attrs; +use russh_sftp::protocol::FileAttributes; +use russh_sftp::protocol::Handle; +use russh_sftp::protocol::OpenFlags; +use russh_sftp::protocol::Status; +use russh_sftp::protocol::StatusCode; +use russh_sftp::server::Handler; use std::collections::HashMap; use std::collections::HashSet; use std::io; @@ -8,38 +15,71 @@ use std::path::PathBuf; use std::sync::Arc; use tokio::fs::File; use tokio::fs::OpenOptions; +use tokio::io::AsyncReadExt; use tokio::io::AsyncSeekExt; use tokio::io::AsyncWriteExt; use tokio::io::SeekFrom; use tokio::sync::Mutex; +use tokio::sync::mpsc; -const SSH_FXP_INIT: u8 = 1; -const SSH_FXP_VERSION: u8 = 2; -const SSH_FXP_OPEN: u8 = 3; -const SSH_FXP_CLOSE: u8 = 4; -const SSH_FXP_WRITE: u8 = 6; -const SSH_FXP_STAT: u8 = 17; -const SSH_FXP_STATUS: u8 = 101; -const SSH_FXP_HANDLE: u8 = 102; -const SSH_FXP_ATTRS: u8 = 105; +const UPLOAD_SFTP_STREAM_BUFFER_BYTES: usize = 64 * 1024; -const SSH_FX_OK: u32 = 0; -const SSH_FX_NO_SUCH_FILE: u32 = 2; -const SSH_FX_PERMISSION_DENIED: u32 = 3; -const SSH_FX_OP_UNSUPPORTED: u32 = 8; +#[derive(Clone)] +pub(crate) struct UploadSftpLane { + incoming_tx: mpsc::Sender>, +} -const SSH_FXF_WRITE: u32 = 0x0000_0002; -const SSH_FXF_APPEND: u32 = 0x0000_0004; -const SSH_FXF_CREAT: u32 = 0x0000_0008; -const SSH_FXF_TRUNC: u32 = 0x0000_0010; +impl UploadSftpLane { + pub(crate) async fn start( + allowed_paths: Arc>>, + binary_writer: mpsc::Sender>, + ) -> Self { + let (server_stream, bridge_stream) = tokio::io::duplex(UPLOAD_SFTP_STREAM_BUFFER_BYTES); + russh_sftp::server::run(server_stream, UploadSftpHandler::new(allowed_paths)).await; -const SSH_FILEXFER_ATTR_SIZE: u32 = 0x0000_0001; -const MAX_SFTP_PACKET_BYTES: usize = 1024 * 1024; + let (mut bridge_reader, mut bridge_writer) = tokio::io::split(bridge_stream); + let (incoming_tx, mut incoming_rx) = mpsc::channel::>(CHANNEL_CAPACITY); -pub(crate) struct UploadSftpSession { + tokio::spawn(async move { + while let Some(bytes) = incoming_rx.recv().await { + if bridge_writer.write_all(&bytes).await.is_err() { + break; + } + } + }); + + tokio::spawn(async move { + let mut buffer = vec![0; UPLOAD_SFTP_STREAM_BUFFER_BYTES]; + loop { + match bridge_reader.read(&mut buffer).await { + Ok(0) | Err(_) => break, + Ok(bytes_read) => { + if binary_writer + .send(buffer[..bytes_read].to_vec()) + .await + .is_err() + { + break; + } + } + } + } + }); + + Self { incoming_tx } + } + + pub(crate) async fn send(&self, bytes: Vec) -> Result<(), JSONRPCErrorError> { + self.incoming_tx + .send(bytes) + .await + .map_err(|_| internal_error("staged upload SFTP lane is closed".to_string())) + } +} + +struct UploadSftpHandler { allowed_paths: Arc>>, - pending_bytes: Vec, - handles: HashMap, UploadHandle>, + handles: HashMap, next_handle_id: u64, } @@ -47,160 +87,44 @@ struct UploadHandle { file: File, } -impl UploadSftpSession { - pub(crate) fn new(allowed_paths: Arc>>) -> Self { +#[derive(Clone, Copy)] +enum UploadSftpError { + NoSuchFile, + PermissionDenied, + Failure, + Unsupported, +} + +impl From for StatusCode { + fn from(value: UploadSftpError) -> Self { + match value { + UploadSftpError::NoSuchFile => Self::NoSuchFile, + UploadSftpError::PermissionDenied => Self::PermissionDenied, + UploadSftpError::Failure => Self::Failure, + UploadSftpError::Unsupported => Self::OpUnsupported, + } + } +} + +impl From for UploadSftpError { + fn from(err: io::Error) -> Self { + match err.kind() { + io::ErrorKind::NotFound => Self::NoSuchFile, + io::ErrorKind::PermissionDenied => Self::PermissionDenied, + _ => Self::Failure, + } + } +} + +impl UploadSftpHandler { + fn new(allowed_paths: Arc>>) -> Self { Self { allowed_paths, - pending_bytes: Vec::new(), handles: HashMap::new(), next_handle_id: 0, } } - pub(crate) async fn process_bytes( - &mut self, - bytes: Vec, - ) -> Result>, JSONRPCErrorError> { - self.pending_bytes.extend(bytes); - let mut responses = Vec::new(); - while let Some(packet) = take_packet(&mut self.pending_bytes)? { - if let Some(response) = self.process_packet(packet).await? { - responses.push(response); - } - } - Ok(responses) - } - - async fn process_packet( - &mut self, - packet: Vec, - ) -> Result>, JSONRPCErrorError> { - let mut cursor = PacketCursor::new(&packet); - let packet_type = cursor.read_u8()?; - match packet_type { - SSH_FXP_INIT => { - let _client_version = cursor.read_u32()?; - cursor.finish()?; - Ok(Some(version_packet())) - } - SSH_FXP_OPEN => { - let request_id = cursor.read_u32()?; - let path = cursor.read_string_utf8()?; - let pflags = cursor.read_u32()?; - let attrs = cursor.read_u32()?; - cursor.finish()?; - if attrs != 0 { - return Ok(Some(status_packet( - request_id, - SSH_FX_OP_UNSUPPORTED, - "open attributes are not supported", - ))); - } - if !self.is_allowed_path(&path).await { - return Ok(Some(status_packet( - request_id, - SSH_FX_PERMISSION_DENIED, - "path is not a staged upload", - ))); - } - let mut options = OpenOptions::new(); - options.write(pflags & SSH_FXF_WRITE != 0); - options.create(pflags & SSH_FXF_CREAT != 0); - options.truncate(pflags & SSH_FXF_TRUNC != 0); - options.append(pflags & SSH_FXF_APPEND != 0); - match options.open(&path).await { - Ok(file) => { - let handle = self.next_handle(); - self.handles.insert(handle.clone(), UploadHandle { file }); - Ok(Some(handle_packet(request_id, &handle))) - } - Err(err) => Ok(Some(status_packet( - request_id, - map_status_code(&err), - &err.to_string(), - ))), - } - } - SSH_FXP_WRITE => { - let request_id = cursor.read_u32()?; - let handle = cursor.read_string_bytes()?; - let offset = cursor.read_u64()?; - let data = cursor.read_string_bytes()?; - cursor.finish()?; - let Some(upload_handle) = self.handles.get_mut(&handle) else { - return Ok(Some(status_packet( - request_id, - SSH_FX_NO_SUCH_FILE, - "unknown upload handle", - ))); - }; - if let Err(err) = upload_handle.file.seek(SeekFrom::Start(offset)).await { - return Ok(Some(status_packet( - request_id, - map_status_code(&err), - &err.to_string(), - ))); - } - match upload_handle.file.write_all(&data).await { - Ok(()) => Ok(Some(status_packet(request_id, SSH_FX_OK, "Ok"))), - Err(err) => Ok(Some(status_packet( - request_id, - map_status_code(&err), - &err.to_string(), - ))), - } - } - SSH_FXP_CLOSE => { - let request_id = cursor.read_u32()?; - let handle = cursor.read_string_bytes()?; - cursor.finish()?; - let Some(mut upload_handle) = self.handles.remove(&handle) else { - return Ok(Some(status_packet( - request_id, - SSH_FX_NO_SUCH_FILE, - "unknown upload handle", - ))); - }; - match upload_handle.file.flush().await { - Ok(()) => Ok(Some(status_packet(request_id, SSH_FX_OK, "Ok"))), - Err(err) => Ok(Some(status_packet( - request_id, - map_status_code(&err), - &err.to_string(), - ))), - } - } - SSH_FXP_STAT => { - let request_id = cursor.read_u32()?; - let path = cursor.read_string_utf8()?; - cursor.finish()?; - if !self.is_allowed_path(&path).await { - return Ok(Some(status_packet( - request_id, - SSH_FX_PERMISSION_DENIED, - "path is not a staged upload", - ))); - } - match tokio::fs::metadata(path).await { - Ok(metadata) => Ok(Some(attrs_packet(request_id, metadata.len()))), - Err(err) => Ok(Some(status_packet( - request_id, - map_status_code(&err), - &err.to_string(), - ))), - } - } - _ => { - let request_id = cursor.read_u32().unwrap_or_default(); - Ok(Some(status_packet( - request_id, - SSH_FX_OP_UNSUPPORTED, - "operation is not supported", - ))) - } - } - } - async fn is_allowed_path(&self, path: &str) -> bool { self.allowed_paths .lock() @@ -208,161 +132,105 @@ impl UploadSftpSession { .contains(&PathBuf::from(path)) } - fn next_handle(&mut self) -> Vec { - let handle = self.next_handle_id.to_be_bytes().to_vec(); + fn next_handle(&mut self) -> String { + let handle = self.next_handle_id.to_string(); self.next_handle_id = self.next_handle_id.wrapping_add(1); handle } } -fn take_packet(buffer: &mut Vec) -> Result>, JSONRPCErrorError> { - if buffer.len() < 4 { - return Ok(None); - } - let packet_len = u32::from_be_bytes( - buffer[..4] - .try_into() - .map_err(|_| invalid_request("invalid SFTP packet length prefix".to_string()))?, - ) as usize; - if packet_len > MAX_SFTP_PACKET_BYTES { - return Err(invalid_sftp_packet("packet exceeds 1 MiB")); - } - if buffer.len() < packet_len + 4 { - return Ok(None); - } - let packet = buffer[4..packet_len + 4].to_vec(); - buffer.drain(..packet_len + 4); - Ok(Some(packet)) -} +impl Handler for UploadSftpHandler { + type Error = UploadSftpError; -fn version_packet() -> Vec { - packet(|payload| { - payload.push(SSH_FXP_VERSION); - payload.extend_from_slice(&3_u32.to_be_bytes()); - }) -} - -fn status_packet(request_id: u32, status_code: u32, message: &str) -> Vec { - packet(|payload| { - payload.push(SSH_FXP_STATUS); - payload.extend_from_slice(&request_id.to_be_bytes()); - payload.extend_from_slice(&status_code.to_be_bytes()); - push_string(payload, message.as_bytes()); - push_string(payload, b"en-US"); - }) -} - -fn handle_packet(request_id: u32, handle: &[u8]) -> Vec { - packet(|payload| { - payload.push(SSH_FXP_HANDLE); - payload.extend_from_slice(&request_id.to_be_bytes()); - push_string(payload, handle); - }) -} - -fn attrs_packet(request_id: u32, size: u64) -> Vec { - packet(|payload| { - payload.push(SSH_FXP_ATTRS); - payload.extend_from_slice(&request_id.to_be_bytes()); - payload.extend_from_slice(&SSH_FILEXFER_ATTR_SIZE.to_be_bytes()); - payload.extend_from_slice(&size.to_be_bytes()); - }) -} - -fn packet(build_payload: impl FnOnce(&mut Vec)) -> Vec { - let mut payload = Vec::new(); - build_payload(&mut payload); - let mut packet = Vec::with_capacity(payload.len() + 4); - packet.extend_from_slice(&(payload.len() as u32).to_be_bytes()); - packet.extend_from_slice(&payload); - packet -} - -fn push_string(payload: &mut Vec, value: &[u8]) { - payload.extend_from_slice(&(value.len() as u32).to_be_bytes()); - payload.extend_from_slice(value); -} - -fn map_status_code(err: &io::Error) -> u32 { - match err.kind() { - io::ErrorKind::NotFound => SSH_FX_NO_SUCH_FILE, - io::ErrorKind::PermissionDenied => SSH_FX_PERMISSION_DENIED, - _ => SSH_FX_OP_UNSUPPORTED, - } -} - -struct PacketCursor<'a> { - bytes: &'a [u8], - offset: usize, -} - -impl<'a> PacketCursor<'a> { - fn new(bytes: &'a [u8]) -> Self { - Self { bytes, offset: 0 } + fn unimplemented(&self) -> Self::Error { + UploadSftpError::Unsupported } - fn read_u8(&mut self) -> Result { - let value = *self - .bytes - .get(self.offset) - .ok_or_else(|| invalid_sftp_packet("missing u8"))?; - self.offset += 1; - Ok(value) - } - - fn read_u32(&mut self) -> Result { - let bytes = self - .bytes - .get(self.offset..self.offset + 4) - .ok_or_else(|| invalid_sftp_packet("missing u32"))?; - self.offset += 4; - Ok(u32::from_be_bytes(bytes.try_into().map_err(|_| { - internal_error("failed to decode SFTP u32".to_string()) - })?)) - } - - fn read_u64(&mut self) -> Result { - let bytes = self - .bytes - .get(self.offset..self.offset + 8) - .ok_or_else(|| invalid_sftp_packet("missing u64"))?; - self.offset += 8; - Ok(u64::from_be_bytes(bytes.try_into().map_err(|_| { - internal_error("failed to decode SFTP u64".to_string()) - })?)) - } - - fn read_string_bytes(&mut self) -> Result, JSONRPCErrorError> { - let len = self.read_u32()? as usize; - let bytes = self - .bytes - .get(self.offset..self.offset + len) - .ok_or_else(|| invalid_sftp_packet("truncated string"))?; - self.offset += len; - Ok(bytes.to_vec()) - } - - fn read_string_utf8(&mut self) -> Result { - String::from_utf8(self.read_string_bytes()?) - .map_err(|_| invalid_sftp_packet("path must be UTF-8")) - } - - fn finish(&self) -> Result<(), JSONRPCErrorError> { - if self.offset == self.bytes.len() { - Ok(()) - } else { - Err(invalid_sftp_packet("unexpected trailing bytes")) + async fn open( + &mut self, + id: u32, + filename: String, + pflags: OpenFlags, + attrs: FileAttributes, + ) -> Result { + if has_attributes(&attrs) { + return Err(UploadSftpError::Unsupported); } + if !self.is_allowed_path(&filename).await { + return Err(UploadSftpError::PermissionDenied); + } + + let mut options = OpenOptions::new(); + options.write(pflags.contains(OpenFlags::WRITE)); + options.create(pflags.contains(OpenFlags::CREATE)); + options.truncate(pflags.contains(OpenFlags::TRUNCATE)); + options.append(pflags.contains(OpenFlags::APPEND)); + let file = options.open(&filename).await?; + let handle = self.next_handle(); + self.handles.insert(handle.clone(), UploadHandle { file }); + Ok(Handle { id, handle }) + } + + async fn write( + &mut self, + id: u32, + handle: String, + offset: u64, + data: Vec, + ) -> Result { + let upload_handle = self + .handles + .get_mut(&handle) + .ok_or(UploadSftpError::NoSuchFile)?; + upload_handle.file.seek(SeekFrom::Start(offset)).await?; + upload_handle.file.write_all(&data).await?; + Ok(ok_status(id)) + } + + async fn close(&mut self, id: u32, handle: String) -> Result { + let mut upload_handle = self + .handles + .remove(&handle) + .ok_or(UploadSftpError::NoSuchFile)?; + upload_handle.file.flush().await?; + Ok(ok_status(id)) + } + + async fn stat(&mut self, id: u32, path: String) -> Result { + if !self.is_allowed_path(&path).await { + return Err(UploadSftpError::PermissionDenied); + } + let metadata = tokio::fs::metadata(path).await?; + let mut attrs = FileAttributes::empty(); + attrs.size = Some(metadata.len()); + Ok(Attrs { id, attrs }) } } -fn invalid_sftp_packet(message: &str) -> JSONRPCErrorError { - invalid_request(format!("invalid staged upload SFTP packet: {message}")) +fn has_attributes(attrs: &FileAttributes) -> bool { + attrs.size.is_some() + || attrs.uid.is_some() + || attrs.user.is_some() + || attrs.gid.is_some() + || attrs.group.is_some() + || attrs.permissions.is_some() + || attrs.atime.is_some() + || attrs.mtime.is_some() +} + +fn ok_status(id: u32) -> Status { + Status { + id, + status_code: StatusCode::Ok, + error_message: "Ok".to_string(), + language_tag: "en-US".to_string(), + } } #[cfg(test)] mod tests { use super::*; + use russh_sftp::client::SftpSession; use tempfile::TempDir; #[tokio::test] @@ -373,38 +241,18 @@ mod tests { .await .expect("create upload parent"); let allowed_paths = Arc::new(Mutex::new(HashSet::from([path.clone()]))); - let mut session = UploadSftpSession::new(allowed_paths); + let (client_stream, server_stream) = tokio::io::duplex(UPLOAD_SFTP_STREAM_BUFFER_BYTES); + russh_sftp::server::run(server_stream, UploadSftpHandler::new(allowed_paths)).await; - let responses = session - .process_bytes(init_packet()) + let session = SftpSession::new(client_stream) .await - .expect("init packet should succeed"); - assert_eq!(packet_type(&responses[0]), SSH_FXP_VERSION); - - let responses = session - .process_bytes(open_packet( - /*request_id*/ 1, - &path, - SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, - )) + .expect("initialize sftp"); + let mut file = session + .create(path.to_string_lossy()) .await - .expect("open packet should succeed"); - assert_eq!(packet_type(&responses[0]), SSH_FXP_HANDLE); - let handle = response_handle(&responses[0]); - - let responses = session - .process_bytes(write_packet( - /*request_id*/ 2, &handle, /*offset*/ 0, b"hello", - )) - .await - .expect("write packet should succeed"); - assert_eq!(response_status(&responses[0]), SSH_FX_OK); - - let responses = session - .process_bytes(close_packet(/*request_id*/ 3, &handle)) - .await - .expect("close packet should succeed"); - assert_eq!(response_status(&responses[0]), SSH_FX_OK); + .expect("open staged upload"); + file.write_all(b"hello").await.expect("write staged upload"); + file.shutdown().await.expect("close staged upload"); assert_eq!( tokio::fs::read_to_string(path).await.expect("read upload"), "hello" @@ -418,65 +266,21 @@ mod tests { tokio::fs::create_dir_all(path.parent().expect("path parent")) .await .expect("create upload parent"); - let mut session = UploadSftpSession::new(Arc::new(Mutex::new(HashSet::new()))); + let (client_stream, server_stream) = tokio::io::duplex(UPLOAD_SFTP_STREAM_BUFFER_BYTES); + russh_sftp::server::run( + server_stream, + UploadSftpHandler::new(Arc::new(Mutex::new(HashSet::new()))), + ) + .await; - let responses = session - .process_bytes(open_packet( - /*request_id*/ 1, - &path, - SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, - )) + let session = SftpSession::new(client_stream) .await - .expect("open packet should be handled"); - assert_eq!(response_status(&responses[0]), SSH_FX_PERMISSION_DENIED); + .expect("initialize sftp"); + let err = match session.create(path.to_string_lossy()).await { + Ok(_) => panic!("path should be rejected"), + Err(err) => err, + }; + assert!(err.to_string().contains("Permission denied")); assert!(!path.exists()); } - - fn init_packet() -> Vec { - packet(|payload| { - payload.push(SSH_FXP_INIT); - payload.extend_from_slice(&3_u32.to_be_bytes()); - }) - } - - fn open_packet(request_id: u32, path: &std::path::Path, flags: u32) -> Vec { - packet(|payload| { - payload.push(SSH_FXP_OPEN); - payload.extend_from_slice(&request_id.to_be_bytes()); - push_string(payload, path.to_string_lossy().as_bytes()); - payload.extend_from_slice(&flags.to_be_bytes()); - payload.extend_from_slice(&0_u32.to_be_bytes()); - }) - } - - fn write_packet(request_id: u32, handle: &[u8], offset: u64, data: &[u8]) -> Vec { - packet(|payload| { - payload.push(SSH_FXP_WRITE); - payload.extend_from_slice(&request_id.to_be_bytes()); - push_string(payload, handle); - payload.extend_from_slice(&offset.to_be_bytes()); - push_string(payload, data); - }) - } - - fn close_packet(request_id: u32, handle: &[u8]) -> Vec { - packet(|payload| { - payload.push(SSH_FXP_CLOSE); - payload.extend_from_slice(&request_id.to_be_bytes()); - push_string(payload, handle); - }) - } - - fn packet_type(packet: &[u8]) -> u8 { - packet[4] - } - - fn response_status(packet: &[u8]) -> u32 { - u32::from_be_bytes(packet[9..13].try_into().expect("status code bytes")) - } - - fn response_handle(packet: &[u8]) -> Vec { - let len = u32::from_be_bytes(packet[9..13].try_into().expect("handle len")) as usize; - packet[13..13 + len].to_vec() - } }