mirror of
https://github.com/openai/codex.git
synced 2026-05-22 12:04:19 +00:00
Compare commits
5 Commits
rust-v0.13
...
efrazer/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80628db404 | ||
|
|
52d5ccb241 | ||
|
|
d140c3a400 | ||
|
|
4c6c974a4b | ||
|
|
381c171490 |
16
MODULE.bazel.lock
generated
16
MODULE.bazel.lock
generated
File diff suppressed because one or more lines are too long
273
codex-rs/Cargo.lock
generated
273
codex-rs/Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
@@ -1180,12 +1180,6 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
@@ -1250,7 +1244,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 +1281,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 +1608,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 +1898,7 @@ dependencies = [
|
||||
"pretty_assertions",
|
||||
"reqwest",
|
||||
"rmcp",
|
||||
"russh-sftp",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serial_test",
|
||||
@@ -4399,7 +4394,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",
|
||||
@@ -4425,18 +4420,6 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
@@ -5045,7 +5028,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",
|
||||
]
|
||||
|
||||
@@ -5150,20 +5133,6 @@ version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.16.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||
dependencies = [
|
||||
"der",
|
||||
"digest",
|
||||
"elliptic-curve",
|
||||
"rfc6979",
|
||||
"signature",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "2.2.3"
|
||||
@@ -5197,26 +5166,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.13.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"crypto-bigint",
|
||||
"digest",
|
||||
"ff",
|
||||
"generic-array",
|
||||
"group",
|
||||
"pem-rfc7468",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.4",
|
||||
"sec1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.3"
|
||||
@@ -5470,16 +5419,6 @@ dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fiat-crypto"
|
||||
version = "0.2.9"
|
||||
@@ -6131,7 +6070,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 +6216,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 +6264,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 +6329,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 +6427,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 +6510,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 +6544,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 +6642,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 +6745,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",
|
||||
@@ -6874,17 +6813,6 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gzip-header"
|
||||
version = "1.0.0"
|
||||
@@ -7688,7 +7616,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 +8047,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 +8120,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 +8510,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 +8554,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 +8566,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 +8579,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 +8622,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 +8640,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 +8825,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 +8854,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 +8866,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 +8887,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 +8898,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 +8931,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 +8949,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 +8962,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 +8973,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 +8985,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 +9073,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 +9101,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 +9293,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]]
|
||||
@@ -9384,18 +9312,6 @@ dependencies = [
|
||||
"supports-color 3.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
|
||||
dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.1"
|
||||
@@ -9675,7 +9591,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",
|
||||
@@ -9825,15 +9741,6 @@ dependencies = [
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "primeorder"
|
||||
version = "0.13.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
|
||||
dependencies = [
|
||||
"elliptic-curve",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.4.0"
|
||||
@@ -9903,7 +9810,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 +9954,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 +10134,7 @@ checksum = "453d60af031e23af2d48995e41b17023f6150044738680508b63671f8d7417dd"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.1",
|
||||
"chrono",
|
||||
"const_format",
|
||||
"csv",
|
||||
@@ -10558,7 +10465,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 +10529,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 +10538,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]]
|
||||
@@ -10784,16 +10691,6 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7"
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
@@ -10888,6 +10785,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 +10889,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 +10902,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 +10971,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",
|
||||
@@ -11253,20 +11168,6 @@ version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"der",
|
||||
"generic-array",
|
||||
"pkcs8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "seccompiler"
|
||||
version = "0.5.0"
|
||||
@@ -11310,7 +11211,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 +11224,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 +11359,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 +11393,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 +11899,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.1",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
@@ -12033,7 +11944,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.10.0",
|
||||
"bitflags 2.11.1",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"crc",
|
||||
@@ -12424,7 +12335,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 +12980,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 +13494,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 +13695,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 +13707,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 +13719,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 +13920,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]]
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -969,6 +969,19 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"FsCreateUploadParams": {
|
||||
"description": "Allocate a Codex-managed host path for a staged upload.",
|
||||
"properties": {
|
||||
"fileName": {
|
||||
"description": "User-visible file name. App-server stores only the final path component.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fileName"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"FsGetMetadataParams": {
|
||||
"description": "Request metadata for an absolute path.",
|
||||
"properties": {
|
||||
@@ -5148,6 +5161,30 @@
|
||||
"title": "Fs/writeFileRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/RequestId"
|
||||
},
|
||||
"method": {
|
||||
"enum": [
|
||||
"fs/createUpload"
|
||||
],
|
||||
"title": "Fs/createUploadRequestMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/FsCreateUploadParams"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "Fs/createUploadRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -6181,4 +6218,4 @@
|
||||
}
|
||||
],
|
||||
"title": "ClientRequest"
|
||||
}
|
||||
}
|
||||
@@ -954,6 +954,30 @@
|
||||
"title": "Fs/writeFileRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/v2/RequestId"
|
||||
},
|
||||
"method": {
|
||||
"enum": [
|
||||
"fs/createUpload"
|
||||
],
|
||||
"title": "Fs/createUploadRequestMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/v2/FsCreateUploadParams"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "Fs/createUploadRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -8687,6 +8711,40 @@
|
||||
"title": "FsCreateDirectoryResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"FsCreateUploadParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Allocate a Codex-managed host path for a staged upload.",
|
||||
"properties": {
|
||||
"fileName": {
|
||||
"description": "User-visible file name. App-server stores only the final path component.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fileName"
|
||||
],
|
||||
"title": "FsCreateUploadParams",
|
||||
"type": "object"
|
||||
},
|
||||
"FsCreateUploadResponse": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Codex-managed host path created by `fs/createUpload`.",
|
||||
"properties": {
|
||||
"path": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "Absolute path where the client should stream staged upload bytes."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"title": "FsCreateUploadResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"FsGetMetadataParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Request metadata for an absolute path.",
|
||||
@@ -18374,4 +18432,4 @@
|
||||
},
|
||||
"title": "CodexAppServerProtocol",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
@@ -1713,6 +1713,30 @@
|
||||
"title": "Fs/writeFileRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/RequestId"
|
||||
},
|
||||
"method": {
|
||||
"enum": [
|
||||
"fs/createUpload"
|
||||
],
|
||||
"title": "Fs/createUploadRequestMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/FsCreateUploadParams"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "Fs/createUploadRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -5143,6 +5167,40 @@
|
||||
"title": "FsCreateDirectoryResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"FsCreateUploadParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Allocate a Codex-managed host path for a staged upload.",
|
||||
"properties": {
|
||||
"fileName": {
|
||||
"description": "User-visible file name. App-server stores only the final path component.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fileName"
|
||||
],
|
||||
"title": "FsCreateUploadParams",
|
||||
"type": "object"
|
||||
},
|
||||
"FsCreateUploadResponse": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Codex-managed host path created by `fs/createUpload`.",
|
||||
"properties": {
|
||||
"path": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "Absolute path where the client should stream staged upload bytes."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"title": "FsCreateUploadResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"FsGetMetadataParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Request metadata for an absolute path.",
|
||||
@@ -16259,4 +16317,4 @@
|
||||
},
|
||||
"title": "CodexAppServerProtocolV2",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
15
codex-rs/app-server-protocol/schema/json/v2/FsCreateUploadParams.json
generated
Normal file
15
codex-rs/app-server-protocol/schema/json/v2/FsCreateUploadParams.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Allocate a Codex-managed host path for a staged upload.",
|
||||
"properties": {
|
||||
"fileName": {
|
||||
"description": "User-visible file name. App-server stores only the final path component.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fileName"
|
||||
],
|
||||
"title": "FsCreateUploadParams",
|
||||
"type": "object"
|
||||
}
|
||||
25
codex-rs/app-server-protocol/schema/json/v2/FsCreateUploadResponse.json
generated
Normal file
25
codex-rs/app-server-protocol/schema/json/v2/FsCreateUploadResponse.json
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"AbsolutePathBuf": {
|
||||
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "Codex-managed host path created by `fs/createUpload`.",
|
||||
"properties": {
|
||||
"path": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "Absolute path where the client should stream staged upload bytes."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"title": "FsCreateUploadResponse",
|
||||
"type": "object"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
12
codex-rs/app-server-protocol/schema/typescript/v2/FsCreateUploadParams.ts
generated
Normal file
12
codex-rs/app-server-protocol/schema/typescript/v2/FsCreateUploadParams.ts
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Allocate a Codex-managed host path for a staged upload.
|
||||
*/
|
||||
export type FsCreateUploadParams = {
|
||||
/**
|
||||
* User-visible file name. App-server stores only the final path component.
|
||||
*/
|
||||
fileName: string, };
|
||||
13
codex-rs/app-server-protocol/schema/typescript/v2/FsCreateUploadResponse.ts
generated
Normal file
13
codex-rs/app-server-protocol/schema/typescript/v2/FsCreateUploadResponse.ts
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
|
||||
/**
|
||||
* Codex-managed host path created by `fs/createUpload`.
|
||||
*/
|
||||
export type FsCreateUploadResponse = {
|
||||
/**
|
||||
* Absolute path where the client should stream staged upload bytes.
|
||||
*/
|
||||
path: AbsolutePathBuf, };
|
||||
@@ -116,6 +116,8 @@ export type { FsCopyParams } from "./FsCopyParams";
|
||||
export type { FsCopyResponse } from "./FsCopyResponse";
|
||||
export type { FsCreateDirectoryParams } from "./FsCreateDirectoryParams";
|
||||
export type { FsCreateDirectoryResponse } from "./FsCreateDirectoryResponse";
|
||||
export type { FsCreateUploadParams } from "./FsCreateUploadParams";
|
||||
export type { FsCreateUploadResponse } from "./FsCreateUploadResponse";
|
||||
export type { FsGetMetadataParams } from "./FsGetMetadataParams";
|
||||
export type { FsGetMetadataResponse } from "./FsGetMetadataResponse";
|
||||
export type { FsReadDirectoryEntry } from "./FsReadDirectoryEntry";
|
||||
|
||||
@@ -664,6 +664,11 @@ client_request_definitions! {
|
||||
serialization: None,
|
||||
response: v2::FsWriteFileResponse,
|
||||
},
|
||||
FsCreateUpload => "fs/createUpload" {
|
||||
params: v2::FsCreateUploadParams,
|
||||
serialization: None,
|
||||
response: v2::FsCreateUploadResponse,
|
||||
},
|
||||
FsCreateDirectory => "fs/createDirectory" {
|
||||
params: v2::FsCreateDirectoryParams,
|
||||
serialization: None,
|
||||
|
||||
@@ -39,6 +39,24 @@ pub struct FsWriteFileParams {
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct FsWriteFileResponse {}
|
||||
|
||||
/// Allocate a Codex-managed host path for a staged upload.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct FsCreateUploadParams {
|
||||
/// User-visible file name. App-server stores only the final path component.
|
||||
pub file_name: String,
|
||||
}
|
||||
|
||||
/// Codex-managed host path created by `fs/createUpload`.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct FsCreateUploadResponse {
|
||||
/// Absolute path where the client should stream staged upload bytes.
|
||||
pub path: AbsolutePathBuf,
|
||||
}
|
||||
|
||||
/// Create a directory on the host filesystem.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -664,6 +664,25 @@ fn fs_read_file_params_round_trip() {
|
||||
assert_eq!(decoded, params);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fs_create_upload_params_round_trip() {
|
||||
let params = FsCreateUploadParams {
|
||||
file_name: "example.bin".to_string(),
|
||||
};
|
||||
|
||||
let value = serde_json::to_value(¶ms).expect("serialize fs/createUpload params");
|
||||
assert_eq!(
|
||||
value,
|
||||
json!({
|
||||
"fileName": "example.bin",
|
||||
})
|
||||
);
|
||||
|
||||
let decoded = serde_json::from_value::<FsCreateUploadParams>(value)
|
||||
.expect("deserialize fs/createUpload params");
|
||||
assert_eq!(decoded, params);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fs_create_directory_params_round_trip_with_default_recursive() {
|
||||
let params = FsCreateDirectoryParams {
|
||||
|
||||
@@ -152,6 +152,8 @@ pub enum TransportEvent {
|
||||
connection_id: ConnectionId,
|
||||
origin: ConnectionOrigin,
|
||||
writer: mpsc::Sender<QueuedOutgoingMessage>,
|
||||
binary_writer: Option<mpsc::Sender<Vec<u8>>>,
|
||||
binary_reader: Option<mpsc::Receiver<Vec<u8>>>,
|
||||
disconnect_sender: Option<CancellationToken>,
|
||||
},
|
||||
ConnectionClosed {
|
||||
@@ -161,6 +163,10 @@ pub enum TransportEvent {
|
||||
connection_id: ConnectionId,
|
||||
message: JSONRPCMessage,
|
||||
},
|
||||
IncomingBinary {
|
||||
connection_id: ConnectionId,
|
||||
bytes: Vec<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
||||
@@ -163,6 +163,8 @@ impl ClientTracker {
|
||||
connection_id,
|
||||
origin: ConnectionOrigin::RemoteControl,
|
||||
writer: writer_tx,
|
||||
binary_writer: None,
|
||||
binary_reader: None,
|
||||
disconnect_sender: Some(disconnect_token.clone()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -34,6 +34,8 @@ pub async fn start_stdio_connection(
|
||||
connection_id,
|
||||
origin: ConnectionOrigin::Stdio,
|
||||
writer: writer_tx,
|
||||
binary_writer: None,
|
||||
binary_reader: None,
|
||||
disconnect_sender: None,
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -53,7 +53,7 @@ fn listen_unix_socket_accepts_relative_custom_path() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn control_socket_acceptor_upgrades_and_forwards_websocket_text_messages_and_pings() {
|
||||
async fn control_socket_acceptor_upgrades_and_forwards_websocket_text_binary_messages_and_pings() {
|
||||
let temp_dir = tempfile::TempDir::new().expect("temp dir");
|
||||
let socket_path = test_socket_path(temp_dir.path());
|
||||
let (transport_event_tx, mut transport_event_rx) =
|
||||
@@ -79,8 +79,12 @@ async fn control_socket_acceptor_upgrades_and_forwards_websocket_text_messages_a
|
||||
.await
|
||||
.expect("connection opened event should arrive")
|
||||
.expect("connection opened event");
|
||||
let connection_id = match opened {
|
||||
TransportEvent::ConnectionOpened { connection_id, .. } => connection_id,
|
||||
let (connection_id, mut binary_reader) = match opened {
|
||||
TransportEvent::ConnectionOpened {
|
||||
connection_id,
|
||||
binary_reader: Some(binary_reader),
|
||||
..
|
||||
} => (connection_id, binary_reader),
|
||||
_ => panic!("expected connection opened event"),
|
||||
};
|
||||
|
||||
@@ -112,6 +116,16 @@ async fn control_socket_acceptor_upgrades_and_forwards_websocket_text_messages_a
|
||||
(connection_id, notification)
|
||||
);
|
||||
|
||||
websocket
|
||||
.send(WebSocketMessage::Binary(Bytes::from_static(b"sftp")))
|
||||
.await
|
||||
.expect("binary payload should send");
|
||||
let incoming_binary = timeout(Duration::from_secs(1), binary_reader.recv())
|
||||
.await
|
||||
.expect("incoming binary payload should arrive")
|
||||
.expect("incoming binary payload");
|
||||
assert_eq!(incoming_binary, b"sftp".to_vec());
|
||||
|
||||
websocket
|
||||
.send(WebSocketMessage::Ping(Bytes::from_static(b"check")))
|
||||
.await
|
||||
|
||||
@@ -182,12 +182,16 @@ pub(crate) async fn run_websocket_connection<M, SinkError, StreamError>(
|
||||
let (writer_tx, writer_rx) =
|
||||
mpsc::channel::<QueuedOutgoingMessage>(WEBSOCKET_OUTBOUND_CHANNEL_CAPACITY);
|
||||
let writer_tx_for_reader = writer_tx.clone();
|
||||
let (binary_writer_tx, binary_writer_rx) = mpsc::channel::<Vec<u8>>(CHANNEL_CAPACITY);
|
||||
let (binary_reader_tx, binary_reader_rx) = mpsc::channel::<Vec<u8>>(CHANNEL_CAPACITY);
|
||||
let disconnect_token = CancellationToken::new();
|
||||
if transport_event_tx
|
||||
.send(TransportEvent::ConnectionOpened {
|
||||
connection_id,
|
||||
origin: ConnectionOrigin::WebSocket,
|
||||
writer: writer_tx,
|
||||
binary_writer: Some(binary_writer_tx.clone()),
|
||||
binary_reader: Some(binary_reader_rx),
|
||||
disconnect_sender: Some(disconnect_token.clone()),
|
||||
})
|
||||
.await
|
||||
@@ -201,6 +205,7 @@ pub(crate) async fn run_websocket_connection<M, SinkError, StreamError>(
|
||||
websocket_writer,
|
||||
writer_rx,
|
||||
writer_control_rx,
|
||||
binary_writer_rx,
|
||||
disconnect_token.clone(),
|
||||
));
|
||||
let mut inbound_task = tokio::spawn(run_websocket_inbound_loop(
|
||||
@@ -208,6 +213,7 @@ pub(crate) async fn run_websocket_connection<M, SinkError, StreamError>(
|
||||
transport_event_tx.clone(),
|
||||
writer_tx_for_reader,
|
||||
writer_control_tx,
|
||||
binary_reader_tx,
|
||||
connection_id,
|
||||
disconnect_token.clone(),
|
||||
));
|
||||
@@ -230,7 +236,7 @@ pub(crate) async fn run_websocket_connection<M, SinkError, StreamError>(
|
||||
|
||||
pub(crate) enum IncomingWebSocketMessage {
|
||||
Text(String),
|
||||
Binary,
|
||||
Binary(Vec<u8>),
|
||||
Ping(Bytes),
|
||||
Pong,
|
||||
Close,
|
||||
@@ -241,6 +247,7 @@ pub(crate) enum IncomingWebSocketMessage {
|
||||
/// sends directly.
|
||||
pub(crate) trait AppServerWebSocketMessage: Sized {
|
||||
fn text(text: String) -> Self;
|
||||
fn binary(payload: Vec<u8>) -> Self;
|
||||
fn pong(payload: Bytes) -> Self;
|
||||
fn into_incoming(self) -> Option<IncomingWebSocketMessage>;
|
||||
}
|
||||
@@ -250,6 +257,10 @@ impl AppServerWebSocketMessage for AxumWebSocketMessage {
|
||||
Self::Text(text.into())
|
||||
}
|
||||
|
||||
fn binary(payload: Vec<u8>) -> Self {
|
||||
Self::Binary(payload.into())
|
||||
}
|
||||
|
||||
fn pong(payload: Bytes) -> Self {
|
||||
Self::Pong(payload)
|
||||
}
|
||||
@@ -257,7 +268,7 @@ impl AppServerWebSocketMessage for AxumWebSocketMessage {
|
||||
fn into_incoming(self) -> Option<IncomingWebSocketMessage> {
|
||||
Some(match self {
|
||||
Self::Text(text) => IncomingWebSocketMessage::Text(text.to_string()),
|
||||
Self::Binary(_) => IncomingWebSocketMessage::Binary,
|
||||
Self::Binary(payload) => IncomingWebSocketMessage::Binary(payload.to_vec()),
|
||||
Self::Ping(payload) => IncomingWebSocketMessage::Ping(payload),
|
||||
Self::Pong(_) => IncomingWebSocketMessage::Pong,
|
||||
Self::Close(_) => IncomingWebSocketMessage::Close,
|
||||
@@ -270,6 +281,10 @@ impl AppServerWebSocketMessage for TungsteniteWebSocketMessage {
|
||||
Self::Text(text.into())
|
||||
}
|
||||
|
||||
fn binary(payload: Vec<u8>) -> Self {
|
||||
Self::Binary(payload.into())
|
||||
}
|
||||
|
||||
fn pong(payload: Bytes) -> Self {
|
||||
Self::Pong(payload)
|
||||
}
|
||||
@@ -277,7 +292,7 @@ impl AppServerWebSocketMessage for TungsteniteWebSocketMessage {
|
||||
fn into_incoming(self) -> Option<IncomingWebSocketMessage> {
|
||||
Some(match self {
|
||||
Self::Text(text) => IncomingWebSocketMessage::Text(text.to_string()),
|
||||
Self::Binary(_) => IncomingWebSocketMessage::Binary,
|
||||
Self::Binary(payload) => IncomingWebSocketMessage::Binary(payload.to_vec()),
|
||||
Self::Ping(payload) => IncomingWebSocketMessage::Ping(payload),
|
||||
Self::Pong(_) => IncomingWebSocketMessage::Pong,
|
||||
Self::Close(_) => IncomingWebSocketMessage::Close,
|
||||
@@ -290,6 +305,7 @@ async fn run_websocket_outbound_loop<M, SinkError>(
|
||||
websocket_writer: impl futures::sink::Sink<M, Error = SinkError> + Send + 'static,
|
||||
mut writer_rx: mpsc::Receiver<QueuedOutgoingMessage>,
|
||||
mut writer_control_rx: mpsc::Receiver<M>,
|
||||
mut binary_writer_rx: mpsc::Receiver<Vec<u8>>,
|
||||
disconnect_token: CancellationToken,
|
||||
) where
|
||||
M: AppServerWebSocketMessage + Send + 'static,
|
||||
@@ -309,6 +325,14 @@ async fn run_websocket_outbound_loop<M, SinkError>(
|
||||
break;
|
||||
}
|
||||
}
|
||||
payload = binary_writer_rx.recv() => {
|
||||
let Some(payload) = payload else {
|
||||
break;
|
||||
};
|
||||
if websocket_writer.send(M::binary(payload)).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
queued_message = writer_rx.recv() => {
|
||||
let Some(queued_message) = queued_message else {
|
||||
break;
|
||||
@@ -332,6 +356,7 @@ async fn run_websocket_inbound_loop<M, StreamError>(
|
||||
transport_event_tx: mpsc::Sender<TransportEvent>,
|
||||
writer_tx_for_reader: mpsc::Sender<QueuedOutgoingMessage>,
|
||||
writer_control_tx: mpsc::Sender<M>,
|
||||
binary_reader_tx: mpsc::Sender<Vec<u8>>,
|
||||
connection_id: ConnectionId,
|
||||
disconnect_token: CancellationToken,
|
||||
) where
|
||||
@@ -371,8 +396,10 @@ async fn run_websocket_inbound_loop<M, StreamError>(
|
||||
}
|
||||
Some(IncomingWebSocketMessage::Pong) => {}
|
||||
Some(IncomingWebSocketMessage::Close) => break,
|
||||
Some(IncomingWebSocketMessage::Binary) => {
|
||||
warn!("dropping unsupported binary websocket message");
|
||||
Some(IncomingWebSocketMessage::Binary(bytes)) => {
|
||||
if binary_reader_tx.send(bytes).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
},
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -189,6 +189,7 @@ Example with notification opt-out:
|
||||
- `process/exited` — experimental; notification emitted when a `process/spawn` session exits.
|
||||
- `fs/readFile` — read an absolute file path and return `{ dataBase64 }`.
|
||||
- `fs/writeFile` — write an absolute file path from base64-encoded `{ dataBase64 }`; returns `{}`.
|
||||
- `fs/createUpload` — allocate a Codex-managed host path for a staged upload and return the absolute host `{ path }`.
|
||||
- `fs/createDirectory` — create an absolute directory path; `recursive` defaults to `true`.
|
||||
- `fs/getMetadata` — return metadata for an absolute path: `isDirectory`, `isFile`, `isSymlink`, `createdAtMs`, and `modifiedAtMs`.
|
||||
- `fs/readDirectory` — list direct child entries for an absolute directory path; each entry contains `fileName`, `isDirectory`, and `isFile`, and `fileName` is just the child name, not a path.
|
||||
@@ -1101,20 +1102,26 @@ All filesystem paths in this section must be absolute.
|
||||
"dataBase64": "aGVsbG8="
|
||||
} }
|
||||
{ "id": 41, "result": {} }
|
||||
{ "method": "fs/getMetadata", "id": 42, "params": {
|
||||
"path": "/tmp/example/nested/note.txt"
|
||||
{ "method": "fs/createUpload", "id": 42, "params": {
|
||||
"fileName": "notes.txt"
|
||||
} }
|
||||
{ "id": 42, "result": {
|
||||
"path": "/Users/me/.codex/uploads/019a7f95-9d35-75e2-9d0e-82bd8cf1af58/notes.txt"
|
||||
} }
|
||||
{ "method": "fs/getMetadata", "id": 43, "params": {
|
||||
"path": "/tmp/example/nested/note.txt"
|
||||
} }
|
||||
{ "id": 43, "result": {
|
||||
"isDirectory": false,
|
||||
"isFile": true,
|
||||
"isSymlink": false,
|
||||
"createdAtMs": 1730910000000,
|
||||
"modifiedAtMs": 1730910000000
|
||||
} }
|
||||
{ "method": "fs/readFile", "id": 43, "params": {
|
||||
{ "method": "fs/readFile", "id": 44, "params": {
|
||||
"path": "/tmp/example/nested/note.txt"
|
||||
} }
|
||||
{ "id": 43, "result": {
|
||||
{ "id": 44, "result": {
|
||||
"dataBase64": "aGVsbG8="
|
||||
} }
|
||||
```
|
||||
@@ -1123,6 +1130,8 @@ All filesystem paths in this section must be absolute.
|
||||
- `fs/createDirectory` defaults `recursive` to `true` when omitted.
|
||||
- `fs/remove` defaults both `recursive` and `force` to `true` when omitted.
|
||||
- `fs/readFile` always returns base64 bytes via `dataBase64`, and `fs/writeFile` always expects base64 bytes in `dataBase64`.
|
||||
- `fs/createUpload` accepts a client-visible `fileName`, stores only its final path component, and chooses the destination under `$CODEX_HOME/uploads/<uuid>/`.
|
||||
- Direct websocket transports keep JSON-RPC on text frames and reserve binary frames for the staged-upload SFTP lane. The SFTP lane is scoped to paths allocated by `fs/createUpload`.
|
||||
- `fs/copy` handles both file copies and directory-tree copies; it requires `recursive: true` when `sourcePath` is a directory. Recursive copies traverse regular files, directories, and symlinks; other entry types are skipped.
|
||||
|
||||
### Example: Filesystem watch
|
||||
|
||||
@@ -828,6 +828,8 @@ pub async fn run_main_with_transport_options(
|
||||
connection_id,
|
||||
origin,
|
||||
writer,
|
||||
binary_writer,
|
||||
binary_reader,
|
||||
disconnect_sender,
|
||||
} => {
|
||||
let outbound_initialized = Arc::new(AtomicBool::new(false));
|
||||
@@ -853,6 +855,28 @@ pub async fn run_main_with_transport_options(
|
||||
{
|
||||
break;
|
||||
}
|
||||
if let (Some(binary_writer), Some(mut binary_reader)) =
|
||||
(binary_writer.clone(), binary_reader)
|
||||
{
|
||||
let processor = Arc::clone(&processor);
|
||||
tokio::spawn(async move {
|
||||
while let Some(bytes) = binary_reader.recv().await {
|
||||
if let Err(err) = processor
|
||||
.process_upload_binary(
|
||||
connection_id,
|
||||
binary_writer.clone(),
|
||||
bytes,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"failed to process upload binary payload: {}",
|
||||
err.message
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
connections.insert(
|
||||
connection_id,
|
||||
ConnectionState::new(
|
||||
@@ -860,6 +884,7 @@ pub async fn run_main_with_transport_options(
|
||||
outbound_initialized,
|
||||
outbound_experimental_api_enabled,
|
||||
outbound_opted_out_notification_methods,
|
||||
binary_writer,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -962,6 +987,25 @@ pub async fn run_main_with_transport_options(
|
||||
}
|
||||
}
|
||||
}
|
||||
TransportEvent::IncomingBinary { connection_id, bytes } => {
|
||||
let Some(connection_state) = connections.get(&connection_id) else {
|
||||
warn!("dropping binary payload from unknown connection: {connection_id:?}");
|
||||
continue;
|
||||
};
|
||||
let Some(binary_writer) = connection_state.binary_writer.clone() else {
|
||||
warn!("dropping binary payload from connection without a binary lane: {connection_id:?}");
|
||||
continue;
|
||||
};
|
||||
match processor
|
||||
.process_upload_binary(connection_id, binary_writer, bytes)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
warn!("failed to process upload binary payload: {}", err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
changed = remote_control_status_rx.changed() => {
|
||||
|
||||
@@ -438,6 +438,7 @@ impl MessageProcessor {
|
||||
.local_environment()
|
||||
.get_filesystem(),
|
||||
fs_watch_manager,
|
||||
config.codex_home.to_path_buf(),
|
||||
);
|
||||
let windows_sandbox_processor = WindowsSandboxRequestProcessor::new(
|
||||
outgoing.clone(),
|
||||
@@ -594,6 +595,17 @@ impl MessageProcessor {
|
||||
tracing::info!("<- typed notification: {:?}", notification);
|
||||
}
|
||||
|
||||
pub(crate) async fn process_upload_binary(
|
||||
&self,
|
||||
connection_id: ConnectionId,
|
||||
binary_writer: tokio::sync::mpsc::Sender<Vec<u8>>,
|
||||
bytes: Vec<u8>,
|
||||
) -> Result<(), JSONRPCErrorError> {
|
||||
self.fs_processor
|
||||
.process_upload_sftp_bytes(connection_id, binary_writer, bytes)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn run_request_with_context<F>(
|
||||
outgoing: Arc<OutgoingMessageSender>,
|
||||
request_context: RequestContext,
|
||||
@@ -860,6 +872,11 @@ impl MessageProcessor {
|
||||
.write_file(params)
|
||||
.await
|
||||
.map(|response| Some(response.into())),
|
||||
ClientRequest::FsCreateUpload { params, .. } => self
|
||||
.fs_processor
|
||||
.create_upload(connection_id, params)
|
||||
.await
|
||||
.map(|response| Some(response.into())),
|
||||
ClientRequest::FsCreateDirectory { params, .. } => self
|
||||
.fs_processor
|
||||
.create_directory(params)
|
||||
|
||||
@@ -448,6 +448,7 @@ mod search;
|
||||
mod thread_processor;
|
||||
mod token_usage_replay;
|
||||
mod turn_processor;
|
||||
mod upload_sftp;
|
||||
mod windows_sandbox_processor;
|
||||
|
||||
pub(crate) use account_processor::AccountRequestProcessor;
|
||||
|
||||
@@ -2,12 +2,15 @@ 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::UploadSftpLane;
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use codex_app_server_protocol::FsCopyParams;
|
||||
use codex_app_server_protocol::FsCopyResponse;
|
||||
use codex_app_server_protocol::FsCreateDirectoryParams;
|
||||
use codex_app_server_protocol::FsCreateDirectoryResponse;
|
||||
use codex_app_server_protocol::FsCreateUploadParams;
|
||||
use codex_app_server_protocol::FsCreateUploadResponse;
|
||||
use codex_app_server_protocol::FsGetMetadataParams;
|
||||
use codex_app_server_protocol::FsGetMetadataResponse;
|
||||
use codex_app_server_protocol::FsReadDirectoryEntry;
|
||||
@@ -28,28 +31,49 @@ use codex_exec_server::CopyOptions;
|
||||
use codex_exec_server::CreateDirectoryOptions;
|
||||
use codex_exec_server::ExecutorFileSystem;
|
||||
use codex_exec_server::RemoveOptions;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
|
||||
type UploadPaths = Arc<Mutex<HashSet<PathBuf>>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct FsRequestProcessor {
|
||||
file_system: Arc<dyn ExecutorFileSystem>,
|
||||
fs_watch_manager: FsWatchManager,
|
||||
codex_home: PathBuf,
|
||||
upload_paths_by_connection: Arc<Mutex<HashMap<ConnectionId, UploadPaths>>>,
|
||||
upload_sftp_lanes: Arc<Mutex<HashMap<ConnectionId, UploadSftpLane>>>,
|
||||
}
|
||||
|
||||
impl FsRequestProcessor {
|
||||
pub(crate) fn new(
|
||||
file_system: Arc<dyn ExecutorFileSystem>,
|
||||
fs_watch_manager: FsWatchManager,
|
||||
codex_home: PathBuf,
|
||||
) -> Self {
|
||||
Self {
|
||||
file_system,
|
||||
fs_watch_manager,
|
||||
codex_home,
|
||||
upload_paths_by_connection: 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_paths_by_connection
|
||||
.lock()
|
||||
.await
|
||||
.remove(&connection_id);
|
||||
self.upload_sftp_lanes.lock().await.remove(&connection_id);
|
||||
}
|
||||
|
||||
pub(crate) async fn read_file(
|
||||
@@ -82,6 +106,81 @@ impl FsRequestProcessor {
|
||||
Ok(FsWriteFileResponse {})
|
||||
}
|
||||
|
||||
pub(crate) async fn create_upload(
|
||||
&self,
|
||||
connection_id: ConnectionId,
|
||||
params: FsCreateUploadParams,
|
||||
) -> Result<FsCreateUploadResponse, JSONRPCErrorError> {
|
||||
let file_name = sanitize_upload_file_name(¶ms.file_name)?;
|
||||
let upload_dir = absolute_path(
|
||||
self.codex_home
|
||||
.join("uploads")
|
||||
.join(Uuid::now_v7().to_string()),
|
||||
)?;
|
||||
let upload_path = absolute_path(upload_dir.as_path().join(file_name))?;
|
||||
self.file_system
|
||||
.create_directory(
|
||||
&upload_dir,
|
||||
CreateDirectoryOptions { recursive: true },
|
||||
/*sandbox*/ None,
|
||||
)
|
||||
.await
|
||||
.map_err(map_fs_error)?;
|
||||
self.upload_paths_for_connection(connection_id)
|
||||
.await
|
||||
.lock()
|
||||
.await
|
||||
.insert(upload_path.as_path().to_path_buf());
|
||||
Ok(FsCreateUploadResponse { path: upload_path })
|
||||
}
|
||||
|
||||
pub(crate) async fn process_upload_sftp_bytes(
|
||||
&self,
|
||||
connection_id: ConnectionId,
|
||||
binary_writer: tokio::sync::mpsc::Sender<Vec<u8>>,
|
||||
bytes: Vec<u8>,
|
||||
) -> Result<(), JSONRPCErrorError> {
|
||||
let lane = if let Some(lane) = self.upload_sftp_lanes.lock().await.get(&connection_id) {
|
||||
lane.clone()
|
||||
} else {
|
||||
let lane = UploadSftpLane::start(
|
||||
self.upload_paths_for_connection(connection_id).await,
|
||||
binary_writer.clone(),
|
||||
)
|
||||
.await;
|
||||
self.upload_sftp_lanes
|
||||
.lock()
|
||||
.await
|
||||
.entry(connection_id)
|
||||
.or_insert_with(|| lane.clone())
|
||||
.clone()
|
||||
};
|
||||
match lane.send(bytes.clone()).await {
|
||||
Ok(()) => Ok(()),
|
||||
Err(_) => {
|
||||
let lane = UploadSftpLane::start(
|
||||
self.upload_paths_for_connection(connection_id).await,
|
||||
binary_writer,
|
||||
)
|
||||
.await;
|
||||
self.upload_sftp_lanes
|
||||
.lock()
|
||||
.await
|
||||
.insert(connection_id, lane.clone());
|
||||
lane.send(bytes).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn upload_paths_for_connection(&self, connection_id: ConnectionId) -> UploadPaths {
|
||||
self.upload_paths_by_connection
|
||||
.lock()
|
||||
.await
|
||||
.entry(connection_id)
|
||||
.or_insert_with(|| Arc::new(Mutex::new(HashSet::new())))
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub(crate) async fn create_directory(
|
||||
&self,
|
||||
params: FsCreateDirectoryParams,
|
||||
@@ -191,6 +290,18 @@ impl FsRequestProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn sanitize_upload_file_name(file_name: &str) -> Result<&str, JSONRPCErrorError> {
|
||||
Path::new(file_name)
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.filter(|name| !name.is_empty())
|
||||
.ok_or_else(|| invalid_request("fs/createUpload requires a fileName".to_string()))
|
||||
}
|
||||
|
||||
fn absolute_path(path: PathBuf) -> Result<AbsolutePathBuf, JSONRPCErrorError> {
|
||||
AbsolutePathBuf::try_from(path).map_err(|err| internal_error(err.to_string()))
|
||||
}
|
||||
|
||||
fn map_fs_error(err: io::Error) -> JSONRPCErrorError {
|
||||
if err.kind() == io::ErrorKind::InvalidInput {
|
||||
invalid_request(err.to_string())
|
||||
|
||||
304
codex-rs/app-server/src/request_processors/upload_sftp.rs
Normal file
304
codex-rs/app-server/src/request_processors/upload_sftp.rs
Normal file
@@ -0,0 +1,304 @@
|
||||
use crate::error_code::internal_error;
|
||||
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;
|
||||
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 UPLOAD_SFTP_STREAM_BUFFER_BYTES: usize = 64 * 1024;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct UploadSftpLane {
|
||||
incoming_tx: mpsc::Sender<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl UploadSftpLane {
|
||||
pub(crate) async fn start(
|
||||
allowed_paths: Arc<Mutex<HashSet<PathBuf>>>,
|
||||
binary_writer: mpsc::Sender<Vec<u8>>,
|
||||
) -> 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;
|
||||
|
||||
let (mut bridge_reader, mut bridge_writer) = tokio::io::split(bridge_stream);
|
||||
let (incoming_tx, mut incoming_rx) = mpsc::channel::<Vec<u8>>(CHANNEL_CAPACITY);
|
||||
|
||||
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<u8>) -> 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<Mutex<HashSet<PathBuf>>>,
|
||||
handles: HashMap<String, UploadHandle>,
|
||||
next_handle_id: u64,
|
||||
}
|
||||
|
||||
struct UploadHandle {
|
||||
file: File,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum UploadSftpError {
|
||||
NoSuchFile,
|
||||
PermissionDenied,
|
||||
Failure,
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
impl From<UploadSftpError> 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<io::Error> 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<Mutex<HashSet<PathBuf>>>) -> Self {
|
||||
Self {
|
||||
allowed_paths,
|
||||
handles: HashMap::new(),
|
||||
next_handle_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_allowed_path(&self, path: &str) -> bool {
|
||||
self.allowed_paths
|
||||
.lock()
|
||||
.await
|
||||
.contains(&PathBuf::from(path))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for UploadSftpHandler {
|
||||
type Error = UploadSftpError;
|
||||
|
||||
fn unimplemented(&self) -> Self::Error {
|
||||
UploadSftpError::Unsupported
|
||||
}
|
||||
|
||||
async fn open(
|
||||
&mut self,
|
||||
id: u32,
|
||||
filename: String,
|
||||
pflags: OpenFlags,
|
||||
_attrs: FileAttributes,
|
||||
) -> Result<Handle, Self::Error> {
|
||||
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<u8>,
|
||||
) -> Result<Status, Self::Error> {
|
||||
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<Status, Self::Error> {
|
||||
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<Attrs, Self::Error> {
|
||||
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 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]
|
||||
async fn writes_only_paths_allocated_for_uploads() {
|
||||
let tempdir = TempDir::new().expect("create temp dir");
|
||||
let path = tempdir.path().join("uploads").join("note.txt");
|
||||
tokio::fs::create_dir_all(path.parent().expect("path parent"))
|
||||
.await
|
||||
.expect("create upload parent");
|
||||
let allowed_paths = Arc::new(Mutex::new(HashSet::from([path.clone()])));
|
||||
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 session = SftpSession::new(client_stream)
|
||||
.await
|
||||
.expect("initialize sftp");
|
||||
let mut file = session
|
||||
.create(path.to_string_lossy())
|
||||
.await
|
||||
.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"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rejects_paths_that_were_not_allocated() {
|
||||
let tempdir = TempDir::new().expect("create temp dir");
|
||||
let path = tempdir.path().join("uploads").join("note.txt");
|
||||
tokio::fs::create_dir_all(path.parent().expect("path parent"))
|
||||
.await
|
||||
.expect("create upload parent");
|
||||
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 session = SftpSession::new(client_stream)
|
||||
.await
|
||||
.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());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn accepts_create_attributes_for_allocated_paths() {
|
||||
let tempdir = TempDir::new().expect("create temp dir");
|
||||
let path = tempdir.path().join("uploads").join("note.txt");
|
||||
tokio::fs::create_dir_all(path.parent().expect("path parent"))
|
||||
.await
|
||||
.expect("create upload parent");
|
||||
let allowed_paths = Arc::new(Mutex::new(HashSet::from([path.clone()])));
|
||||
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 session = SftpSession::new(client_stream)
|
||||
.await
|
||||
.expect("initialize sftp");
|
||||
let mut attrs = FileAttributes::empty();
|
||||
attrs.size = Some(0);
|
||||
let mut file = session
|
||||
.open_with_flags_and_attributes(
|
||||
path.to_string_lossy(),
|
||||
OpenFlags::CREATE | OpenFlags::TRUNCATE | OpenFlags::WRITE,
|
||||
attrs,
|
||||
)
|
||||
.await
|
||||
.expect("open staged upload with attrs");
|
||||
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"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ pub(crate) struct ConnectionState {
|
||||
pub(crate) outbound_initialized: Arc<AtomicBool>,
|
||||
pub(crate) outbound_experimental_api_enabled: Arc<AtomicBool>,
|
||||
pub(crate) outbound_opted_out_notification_methods: Arc<RwLock<HashSet<String>>>,
|
||||
pub(crate) binary_writer: Option<mpsc::Sender<Vec<u8>>>,
|
||||
pub(crate) session: Arc<ConnectionSessionState>,
|
||||
}
|
||||
|
||||
@@ -40,11 +41,13 @@ impl ConnectionState {
|
||||
outbound_initialized: Arc<AtomicBool>,
|
||||
outbound_experimental_api_enabled: Arc<AtomicBool>,
|
||||
outbound_opted_out_notification_methods: Arc<RwLock<HashSet<String>>>,
|
||||
binary_writer: Option<mpsc::Sender<Vec<u8>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
outbound_initialized,
|
||||
outbound_experimental_api_enabled,
|
||||
outbound_opted_out_notification_methods,
|
||||
binary_writer,
|
||||
session: Arc::new(ConnectionSessionState::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ use codex_app_server_protocol::ExperimentalFeatureListParams;
|
||||
use codex_app_server_protocol::FeedbackUploadParams;
|
||||
use codex_app_server_protocol::FsCopyParams;
|
||||
use codex_app_server_protocol::FsCreateDirectoryParams;
|
||||
use codex_app_server_protocol::FsCreateUploadParams;
|
||||
use codex_app_server_protocol::FsGetMetadataParams;
|
||||
use codex_app_server_protocol::FsReadDirectoryParams;
|
||||
use codex_app_server_protocol::FsReadFileParams;
|
||||
@@ -988,6 +989,14 @@ impl McpProcess {
|
||||
self.send_request("fs/writeFile", params).await
|
||||
}
|
||||
|
||||
pub async fn send_fs_create_upload_request(
|
||||
&mut self,
|
||||
params: FsCreateUploadParams,
|
||||
) -> anyhow::Result<i64> {
|
||||
let params = Some(serde_json::to_value(params)?);
|
||||
self.send_request("fs/createUpload", params).await
|
||||
}
|
||||
|
||||
pub async fn send_fs_create_directory_request(
|
||||
&mut self,
|
||||
params: FsCreateDirectoryParams,
|
||||
|
||||
@@ -6,6 +6,8 @@ use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use codex_app_server_protocol::FsChangedNotification;
|
||||
use codex_app_server_protocol::FsCopyParams;
|
||||
use codex_app_server_protocol::FsCreateUploadParams;
|
||||
use codex_app_server_protocol::FsCreateUploadResponse;
|
||||
use codex_app_server_protocol::FsGetMetadataResponse;
|
||||
use codex_app_server_protocol::FsReadDirectoryEntry;
|
||||
use codex_app_server_protocol::FsReadFileResponse;
|
||||
@@ -299,6 +301,37 @@ async fn fs_methods_cover_current_fs_utils_surface() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn fs_create_upload_allocates_codex_managed_storage() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut mcp = initialized_mcp(&codex_home).await?;
|
||||
|
||||
let request_id = mcp
|
||||
.send_fs_create_upload_request(FsCreateUploadParams {
|
||||
file_name: "../note.txt".to_string(),
|
||||
})
|
||||
.await?;
|
||||
let response: FsCreateUploadResponse = to_response(
|
||||
timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
|
||||
)
|
||||
.await??,
|
||||
)?;
|
||||
|
||||
let path = response.path.as_path();
|
||||
let parent = path.parent().expect("upload path should have a parent");
|
||||
let canonical_parent = std::fs::canonicalize(parent)?;
|
||||
let canonical_codex_home = std::fs::canonicalize(codex_home.path())?;
|
||||
assert!(canonical_parent.starts_with(canonical_codex_home.join("uploads")));
|
||||
assert_eq!(
|
||||
path.file_name().and_then(|name| name.to_str()),
|
||||
Some("note.txt")
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn fs_write_file_accepts_base64_bytes() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
Reference in New Issue
Block a user