diff --git a/Cargo.Bazel.json.lock b/Cargo.Bazel.json.lock index 92984b8e5b92..eb3ab7bac04a 100644 --- a/Cargo.Bazel.json.lock +++ b/Cargo.Bazel.json.lock @@ -1,5 +1,5 @@ { - "checksum": "973d5e40f08086e83617bf7cc893f4c4fbc17cd1d8798002a0f3c65eec39fe79", + "checksum": "50730c1bad9c51144f974cc56b36ae127cd53d03a5e7e45e300d1bab585124fe", "crates": { "abnf 0.12.0": { "name": "abnf", @@ -201,11 +201,11 @@ "target": "bytes" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { @@ -217,7 +217,7 @@ "target": "pin_project_lite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -333,7 +333,7 @@ "target": "flate2" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -389,7 +389,7 @@ "target": "smallvec" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -597,11 +597,11 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -676,11 +676,11 @@ "target": "actix_utils" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -696,7 +696,7 @@ "target": "socket2" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -748,7 +748,7 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -929,11 +929,11 @@ "target": "encoding_rs" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -1132,7 +1132,7 @@ "target": "actix_web" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -2258,7 +2258,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -3864,7 +3864,7 @@ "selects": { "cfg(any())": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -3970,7 +3970,7 @@ "target": "event_listener" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" } ], @@ -4033,7 +4033,7 @@ "target": "event_listener_strategy" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -4106,7 +4106,7 @@ "target": "flate2" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -4118,7 +4118,7 @@ "target": "pin_project_lite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -4179,7 +4179,7 @@ "target": "anyhow" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -4251,7 +4251,7 @@ "target": "concurrent_queue" }, { - "id": "futures-io 0.3.31", + "id": "futures-io 0.3.32", "target": "futures_io" }, { @@ -4509,7 +4509,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -4557,7 +4557,7 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -4837,7 +4837,7 @@ "target": "async_net" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -4960,7 +4960,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -5184,7 +5184,7 @@ "target": "bytes" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -5252,7 +5252,7 @@ "target": "sync_wrapper" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -5365,7 +5365,7 @@ "target": "form_urlencoded" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -5437,7 +5437,7 @@ "target": "sync_wrapper" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -5527,7 +5527,7 @@ "target": "bytes" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -5633,7 +5633,7 @@ "target": "bytes" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -5745,7 +5745,7 @@ "target": "bytes" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -5969,7 +5969,7 @@ "target": "axum" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -6108,7 +6108,7 @@ "target": "rustls_pki_types" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -6174,7 +6174,7 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -6194,7 +6194,7 @@ "target": "rand" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio", "alias": "tokio_1" } @@ -6247,7 +6247,7 @@ "target": "fastrand" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -6255,7 +6255,7 @@ "target": "pin_project" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -6333,7 +6333,7 @@ "target": "cfg_if" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -9065,7 +9065,7 @@ "target": "async_task" }, { - "id": "futures-io 0.3.31", + "id": "futures-io 0.3.32", "target": "futures_io" }, { @@ -10054,7 +10054,7 @@ "target": "cargo_metadata" }, { - "id": "glob 0.3.1", + "id": "glob 0.3.3", "target": "glob" }, { @@ -11063,7 +11063,7 @@ "target": "build_script_build" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -12523,7 +12523,7 @@ "target": "jobserver" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -12533,7 +12533,7 @@ "target": "jobserver" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -12543,7 +12543,7 @@ "target": "jobserver" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -12553,7 +12553,7 @@ "target": "jobserver" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -12730,7 +12730,7 @@ "target": "serde_wasm_bindgen" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ], @@ -13236,11 +13236,11 @@ ], "wasm32-unknown-unknown": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ], @@ -13625,11 +13625,11 @@ "target": "build_script_build" }, { - "id": "glob 0.3.1", + "id": "glob 0.3.3", "target": "glob" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -13655,7 +13655,7 @@ "deps": { "common": [ { - "id": "glob 0.3.1", + "id": "glob 0.3.3", "target": "glob" } ], @@ -14171,7 +14171,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -15128,7 +15128,7 @@ "target": "lazy_static" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -15216,7 +15216,7 @@ "target": "lazy_static" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -15282,7 +15282,7 @@ "target": "cfg_if" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ], @@ -15671,7 +15671,7 @@ "target": "core_foundation_sys" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -15730,7 +15730,7 @@ "target": "core_foundation_sys" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -15881,19 +15881,19 @@ "selects": { "aarch64-linux-android": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(all(target_arch = \"aarch64\", target_os = \"linux\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(all(target_arch = \"aarch64\", target_vendor = \"apple\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -16742,7 +16742,7 @@ "selects": { "cfg(target_arch = \"riscv64\")": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -17030,7 +17030,7 @@ "target": "criterion_plot" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -17078,7 +17078,7 @@ "target": "tinytemplate" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -17641,7 +17641,7 @@ "selects": { "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -17708,7 +17708,7 @@ "selects": { "aarch64-apple-darwin": [ { - "id": "mio 1.0.2", + "id": "mio 1.2.0", "target": "mio" }, { @@ -17722,7 +17722,7 @@ ], "aarch64-unknown-linux-gnu": [ { - "id": "mio 1.0.2", + "id": "mio 1.2.0", "target": "mio" }, { @@ -17742,7 +17742,7 @@ ], "x86_64-apple-darwin": [ { - "id": "mio 1.0.2", + "id": "mio 1.2.0", "target": "mio" }, { @@ -17756,7 +17756,7 @@ ], "x86_64-unknown-linux-gnu": [ { - "id": "mio 1.0.2", + "id": "mio 1.2.0", "target": "mio" }, { @@ -19762,7 +19762,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -19831,7 +19831,7 @@ "target": "dbus" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -21652,11 +21652,11 @@ "target": "fs_extra" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -21965,7 +21965,7 @@ "target": "leb128" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -22039,6 +22039,10 @@ "id": "metrics-proxy 0.1.0", "target": "metrics_proxy" }, + { + "id": "meval 0.2.0", + "target": "meval" + }, { "id": "minicbor 0.19.1", "target": "minicbor" @@ -22224,6 +22228,10 @@ "id": "quinn 0.11.5", "target": "quinn" }, + { + "id": "quinn-proto 0.11.7", + "target": "quinn_proto" + }, { "id": "quinn-udp 0.5.5", "target": "quinn_udp" @@ -22284,6 +22292,11 @@ "id": "rgb 0.8.37", "target": "rgb" }, + { + "id": "rig-core 0.36.0", + "target": "rig", + "alias": "rig_core" + }, { "id": "ring 0.17.14", "target": "ring" @@ -22566,7 +22579,7 @@ "target": "time" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -22626,7 +22639,7 @@ "target": "tower" }, { - "id": "tower-http 0.6.6", + "id": "tower-http 0.6.8", "target": "tower_http" }, { @@ -22714,7 +22727,7 @@ "target": "warp" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" }, { @@ -23104,7 +23117,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -23171,7 +23184,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -23233,7 +23246,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -25549,19 +25562,19 @@ ], "cfg(target_os = \"hermit\")": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(target_os = \"wasi\")": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -25624,19 +25637,19 @@ "selects": { "cfg(target_os = \"hermit\")": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(target_os = \"wasi\")": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -25706,7 +25719,7 @@ "target": "build_script_build" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -26463,6 +26476,69 @@ ], "license_file": "LICENSE-APACHE" }, + "eventsource-stream 0.2.3": { + "name": "eventsource-stream", + "version": "0.2.3", + "package_url": "https://github.com/jpopesculian/eventsource-stream", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/eventsource-stream/0.2.3/download", + "sha256": "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" + } + }, + "targets": [ + { + "Library": { + "crate_name": "eventsource_stream", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "eventsource_stream", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "futures-core 0.3.32", + "target": "futures_core" + }, + { + "id": "nom 7.1.3", + "target": "nom" + }, + { + "id": "pin-project-lite 0.2.16", + "target": "pin_project_lite" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.2.3" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": null + }, "evm_rpc_client 0.4.0": { "name": "evm_rpc_client", "version": "0.4.0", @@ -26658,7 +26734,7 @@ "target": "errno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -27292,7 +27368,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -27401,7 +27477,7 @@ "target": "build_script_build" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -28504,7 +28580,7 @@ "target": "build_script_build" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -28704,14 +28780,14 @@ ], "license_file": "LICENSE-APACHE" }, - "futures 0.3.31": { + "futures 0.3.32": { "name": "futures", - "version": "0.3.31", + "version": "0.3.32", "package_url": "https://github.com/rust-lang/futures-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/futures/0.3.31/download", - "sha256": "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" + "url": "https://static.crates.io/crates/futures/0.3.32/download", + "sha256": "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" } }, "targets": [ @@ -28747,38 +28823,38 @@ "deps": { "common": [ { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-executor 0.3.31", + "id": "futures-executor 0.3.32", "target": "futures_executor" }, { - "id": "futures-io 0.3.31", + "id": "futures-io 0.3.32", "target": "futures_io" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { - "id": "futures-task 0.3.31", + "id": "futures-task 0.3.32", "target": "futures_task" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" } ], "selects": {} }, "edition": "2018", - "version": "0.3.31" + "version": "0.3.32" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -28787,14 +28863,14 @@ ], "license_file": "LICENSE-APACHE" }, - "futures-channel 0.3.31": { + "futures-channel 0.3.32": { "name": "futures-channel", - "version": "0.3.31", + "version": "0.3.32", "package_url": "https://github.com/rust-lang/futures-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/futures-channel/0.3.31/download", - "sha256": "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" + "url": "https://static.crates.io/crates/futures-channel/0.3.32/download", + "sha256": "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" } }, "targets": [ @@ -28829,18 +28905,18 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" } ], "selects": {} }, "edition": "2018", - "version": "0.3.31" + "version": "0.3.32" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -28849,14 +28925,14 @@ ], "license_file": "LICENSE-APACHE" }, - "futures-core 0.3.31": { + "futures-core 0.3.32": { "name": "futures-core", - "version": "0.3.31", + "version": "0.3.32", "package_url": "https://github.com/rust-lang/futures-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/futures-core/0.3.31/download", - "sha256": "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + "url": "https://static.crates.io/crates/futures-core/0.3.32/download", + "sha256": "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" } }, "targets": [ @@ -28887,7 +28963,7 @@ "selects": {} }, "edition": "2018", - "version": "0.3.31" + "version": "0.3.32" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -28896,14 +28972,14 @@ ], "license_file": "LICENSE-APACHE" }, - "futures-executor 0.3.31": { + "futures-executor 0.3.32": { "name": "futures-executor", - "version": "0.3.31", + "version": "0.3.32", "package_url": "https://github.com/rust-lang/futures-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/futures-executor/0.3.31/download", - "sha256": "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" + "url": "https://static.crates.io/crates/futures-executor/0.3.32/download", + "sha256": "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" } }, "targets": [ @@ -28935,22 +29011,22 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-task 0.3.31", + "id": "futures-task 0.3.32", "target": "futures_task" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" } ], "selects": {} }, "edition": "2018", - "version": "0.3.31" + "version": "0.3.32" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -28959,14 +29035,14 @@ ], "license_file": "LICENSE-APACHE" }, - "futures-io 0.3.31": { + "futures-io 0.3.32": { "name": "futures-io", - "version": "0.3.31", + "version": "0.3.32", "package_url": "https://github.com/rust-lang/futures-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/futures-io/0.3.31/download", - "sha256": "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + "url": "https://static.crates.io/crates/futures-io/0.3.32/download", + "sha256": "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" } }, "targets": [ @@ -28996,7 +29072,7 @@ "selects": {} }, "edition": "2018", - "version": "0.3.31" + "version": "0.3.32" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -29055,11 +29131,11 @@ "target": "fastrand" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-io 0.3.31", + "id": "futures-io 0.3.32", "target": "futures_io" }, { @@ -29091,14 +29167,14 @@ ], "license_file": "LICENSE-APACHE" }, - "futures-macro 0.3.31": { + "futures-macro 0.3.32": { "name": "futures-macro", - "version": "0.3.31", + "version": "0.3.32", "package_url": "https://github.com/rust-lang/futures-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/futures-macro/0.3.31/download", - "sha256": "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" + "url": "https://static.crates.io/crates/futures-macro/0.3.32/download", + "sha256": "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" } }, "targets": [ @@ -29138,7 +29214,7 @@ "selects": {} }, "edition": "2018", - "version": "0.3.31" + "version": "0.3.32" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -29186,7 +29262,7 @@ "deps": { "common": [ { - "id": "futures-io 0.3.31", + "id": "futures-io 0.3.32", "target": "futures_io" }, { @@ -29211,14 +29287,14 @@ ], "license_file": "LICENSE-APACHE" }, - "futures-sink 0.3.31": { + "futures-sink 0.3.32": { "name": "futures-sink", - "version": "0.3.31", + "version": "0.3.32", "package_url": "https://github.com/rust-lang/futures-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/futures-sink/0.3.31/download", - "sha256": "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + "url": "https://static.crates.io/crates/futures-sink/0.3.32/download", + "sha256": "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" } }, "targets": [ @@ -29249,7 +29325,7 @@ "selects": {} }, "edition": "2018", - "version": "0.3.31" + "version": "0.3.32" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -29258,14 +29334,14 @@ ], "license_file": "LICENSE-APACHE" }, - "futures-task 0.3.31": { + "futures-task 0.3.32": { "name": "futures-task", - "version": "0.3.31", + "version": "0.3.32", "package_url": "https://github.com/rust-lang/futures-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/futures-task/0.3.31/download", - "sha256": "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + "url": "https://static.crates.io/crates/futures-task/0.3.32/download", + "sha256": "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" } }, "targets": [ @@ -29290,12 +29366,13 @@ "crate_features": { "common": [ "alloc", + "default", "std" ], "selects": {} }, "edition": "2018", - "version": "0.3.31" + "version": "0.3.32" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -29343,14 +29420,14 @@ ], "license_file": "LICENSE-APACHE" }, - "futures-util 0.3.31": { + "futures-util 0.3.32": { "name": "futures-util", - "version": "0.3.31", + "version": "0.3.32", "package_url": "https://github.com/rust-lang/futures-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/futures-util/0.3.31/download", - "sha256": "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" + "url": "https://static.crates.io/crates/futures-util/0.3.32/download", + "sha256": "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" } }, "targets": [ @@ -29394,23 +29471,23 @@ "deps": { "common": [ { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-io 0.3.31", + "id": "futures-io 0.3.32", "target": "futures_io" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { - "id": "futures-task 0.3.31", + "id": "futures-task 0.3.32", "target": "futures_task" }, { @@ -29421,10 +29498,6 @@ "id": "pin-project-lite 0.2.16", "target": "pin_project_lite" }, - { - "id": "pin-utils 0.1.0", - "target": "pin_utils" - }, { "id": "slab 0.4.8", "target": "slab" @@ -29436,13 +29509,13 @@ "proc_macro_deps": { "common": [ { - "id": "futures-macro 0.3.31", + "id": "futures-macro 0.3.32", "target": "futures_macro" } ], "selects": {} }, - "version": "0.3.31" + "version": "0.3.32" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -29700,7 +29773,7 @@ ], "cfg(all(not(windows), not(any(target_os = \"android\", target_os = \"linux\"))))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -29846,7 +29919,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -29924,7 +29997,7 @@ "selects": { "cfg(all(any(target_os = \"linux\", target_os = \"android\"), not(any(getrandom_backend = \"custom\", getrandom_backend = \"rdrand\", getrandom_backend = \"rndr\"))))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -29942,49 +30015,49 @@ ], "cfg(any(target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"hurd\", target_os = \"illumos\", all(target_os = \"horizon\", target_arch = \"arm\")))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(any(target_os = \"haiku\", target_os = \"redox\", target_os = \"nto\", target_os = \"aix\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(any(target_os = \"ios\", target_os = \"visionos\", target_os = \"watchos\", target_os = \"tvos\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(any(target_os = \"macos\", target_os = \"openbsd\", target_os = \"vita\", target_os = \"emscripten\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(target_os = \"netbsd\")": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(target_os = \"solaris\")": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(target_os = \"vxworks\")": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "wasm32-unknown-unknown": [ { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ] @@ -30244,14 +30317,14 @@ ], "license_file": "LICENSE-APACHE" }, - "glob 0.3.1": { + "glob 0.3.3": { "name": "glob", - "version": "0.3.1", + "version": "0.3.3", "package_url": "https://github.com/rust-lang/glob", "repository": { "Http": { - "url": "https://static.crates.io/crates/glob/0.3.1/download", - "sha256": "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + "url": "https://static.crates.io/crates/glob/0.3.3/download", + "sha256": "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" } }, "targets": [ @@ -30274,7 +30347,7 @@ "**" ], "edition": "2015", - "version": "0.3.1" + "version": "0.3.3" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -30393,7 +30466,7 @@ "target": "dashmap" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { @@ -30401,7 +30474,7 @@ "target": "futures_timer" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -30506,7 +30579,7 @@ "target": "dashmap" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { @@ -30514,7 +30587,7 @@ "target": "futures_timer" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -30787,15 +30860,15 @@ "target": "fnv" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -30811,7 +30884,7 @@ "target": "slab" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -30880,15 +30953,15 @@ "target": "fnv" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -30904,7 +30977,7 @@ "target": "slab" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -31943,7 +32016,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -32398,15 +32471,15 @@ "target": "data_encoding" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-io 0.3.31", + "id": "futures-io 0.3.32", "target": "futures_io" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -32458,7 +32531,7 @@ "target": "tinyvec" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -32554,7 +32627,7 @@ "target": "cfg_if" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -32594,7 +32667,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -32819,7 +32892,7 @@ "selects": { "cfg(any(unix, target_os = \"redox\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -32885,7 +32958,7 @@ "selects": { "cfg(any(unix, target_os = \"redox\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -33372,7 +33445,7 @@ "target": "bytes" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -33658,15 +33731,15 @@ "target": "bytes" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -33702,7 +33775,7 @@ "target": "socket2" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -33780,11 +33853,11 @@ "target": "bytes" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -33824,7 +33897,7 @@ "target": "smallvec" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -33889,7 +33962,7 @@ "deps": { "common": [ { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -33913,7 +33986,7 @@ "target": "rustls_native_certs" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -34027,7 +34100,7 @@ "target": "rustls_platform_verifier" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -34108,7 +34181,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -34172,7 +34245,7 @@ "target": "pin_project_lite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -34238,16 +34311,20 @@ ], "selects": { "aarch64-apple-darwin": [ - "client-proxy" + "client-proxy", + "client-proxy-system" ], "aarch64-unknown-linux-gnu": [ - "client-proxy" + "client-proxy", + "client-proxy-system" ], "x86_64-apple-darwin": [ - "client-proxy" + "client-proxy", + "client-proxy-system" ], "x86_64-unknown-linux-gnu": [ - "client-proxy" + "client-proxy", + "client-proxy-system" ] } }, @@ -34258,11 +34335,11 @@ "target": "bytes" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -34278,7 +34355,7 @@ "target": "hyper" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -34290,7 +34367,7 @@ "target": "socket2" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -34315,6 +34392,10 @@ { "id": "percent-encoding 2.3.1", "target": "percent_encoding" + }, + { + "id": "system-configuration 0.6.1", + "target": "system_configuration" } ], "aarch64-unknown-linux-gnu": [ @@ -34343,6 +34424,10 @@ { "id": "percent-encoding 2.3.1", "target": "percent_encoding" + }, + { + "id": "system-configuration 0.6.1", + "target": "system_configuration" } ], "x86_64-unknown-linux-gnu": [ @@ -34416,11 +34501,11 @@ ], "cfg(target_arch = \"wasm32\")": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ], @@ -34623,7 +34708,7 @@ "target": "elliptic_curve" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -34738,7 +34823,7 @@ "selects": { "cfg(not(target_family = \"wasm\"))": [ { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ] @@ -34867,11 +34952,11 @@ "target": "fqdn" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -35019,7 +35104,7 @@ "target": "sha2" }, { - "id": "socket2 0.6.1", + "id": "socket2 0.6.3", "target": "socket2" }, { @@ -35043,7 +35128,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -35247,7 +35332,7 @@ "target": "serde" }, { - "id": "socket2 0.6.1", + "id": "socket2 0.6.3", "target": "socket2" }, { @@ -36271,7 +36356,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -36279,7 +36364,7 @@ "target": "tokio_util" }, { - "id": "tower-http 0.6.6", + "id": "tower-http 0.6.8", "target": "tower_http" }, { @@ -36415,7 +36500,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -36587,7 +36672,7 @@ "target": "ic_custom_domains_canister_api" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -36906,7 +36991,7 @@ "target": "fqdn" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -37022,7 +37107,7 @@ "target": "time" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -37034,7 +37119,7 @@ "target": "tower" }, { - "id": "tower-http 0.6.6", + "id": "tower-http 0.6.8", "target": "tower_http" }, { @@ -37210,7 +37295,7 @@ "target": "candid" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -38098,7 +38183,7 @@ "target": "candid" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -38288,7 +38373,7 @@ "target": "candid" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -38852,7 +38937,7 @@ "target": "candid" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -41132,7 +41217,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -42282,7 +42367,7 @@ "selects": { "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -42298,14 +42383,14 @@ ], "license_file": "LICENSE-APACHE" }, - "js-sys 0.3.77": { + "js-sys 0.3.97": { "name": "js-sys", - "version": "0.3.77", - "package_url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/js-sys", + "version": "0.3.97", + "package_url": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/js-sys", "repository": { "Http": { - "url": "https://static.crates.io/crates/js-sys/0.3.77/download", - "sha256": "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" + "url": "https://static.crates.io/crates/js-sys/0.3.97/download", + "sha256": "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" } }, "targets": [ @@ -42330,25 +42415,34 @@ "crate_features": { "common": [ "default", - "std" + "std", + "unsafe-eval" ], "selects": {} }, "deps": { "common": [ + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "futures-util 0.3.32", + "target": "futures_util" + }, { "id": "once_cell 1.21.3", "target": "once_cell" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ], "selects": {} }, "edition": "2021", - "version": "0.3.77" + "version": "0.3.97" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -44126,14 +44220,14 @@ ], "license_file": "LICENSE-APACHE" }, - "libc 0.2.177": { + "libc 0.2.186": { "name": "libc", - "version": "0.2.177", + "version": "0.2.186", "package_url": "https://github.com/rust-lang/libc", "repository": { "Http": { - "url": "https://static.crates.io/crates/libc/0.2.177/download", - "sha256": "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + "url": "https://static.crates.io/crates/libc/0.2.186/download", + "sha256": "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" } }, "targets": [ @@ -44178,14 +44272,14 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "build_script_build" } ], "selects": {} }, "edition": "2021", - "version": "0.2.177" + "version": "0.2.186" }, "build_script_attrs": { "compile_data_glob": [ @@ -44269,7 +44363,7 @@ "target": "either" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -44991,7 +45085,7 @@ "target": "lazy_static" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -45051,7 +45145,7 @@ "target": "bitflags" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -45117,7 +45211,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -45151,7 +45245,7 @@ "target": "cc" }, { - "id": "glob 0.3.1", + "id": "glob 0.3.3", "target": "glob" } ], @@ -45323,7 +45417,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -45460,7 +45554,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -45547,7 +45641,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -45649,7 +45743,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -45783,7 +45877,7 @@ "target": "bitflags" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -46035,7 +46129,7 @@ "deps": { "common": [ { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -46103,7 +46197,7 @@ "target": "byteorder" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -46181,7 +46275,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -46270,15 +46364,15 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -46343,7 +46437,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -46850,7 +46944,7 @@ "target": "errno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -47137,7 +47231,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -47307,7 +47401,7 @@ "selects": { "cfg(any(target_os = \"macos\", target_os = \"ios\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -48009,7 +48103,7 @@ "selects": { "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -48059,7 +48153,7 @@ "selects": { "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -48376,7 +48470,7 @@ "target": "exitcode" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -48468,7 +48562,7 @@ "target": "simple_logger" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -48497,6 +48591,64 @@ "license_ids": [], "license_file": "LICENSE" }, + "meval 0.2.0": { + "name": "meval", + "version": "0.2.0", + "package_url": "https://github.com/rekka/meval-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/meval/0.2.0/download", + "sha256": "f79496a5651c8d57cd033c5add8ca7ee4e3d5f7587a4777484640d9cb60392d9" + } + }, + "targets": [ + { + "Library": { + "crate_name": "meval", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "meval", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "fnv 1.0.7", + "target": "fnv" + }, + { + "id": "nom 1.2.4", + "target": "nom" + } + ], + "selects": {} + }, + "edition": "2015", + "version": "0.2.0" + }, + "license": "Unlicense/MIT", + "license_ids": [ + "MIT", + "Unlicense" + ], + "license_file": "LICENSE-MIT" + }, "mime 0.3.17": { "name": "mime", "version": "0.3.17", @@ -48536,14 +48688,14 @@ ], "license_file": "LICENSE-APACHE" }, - "mime_guess 2.0.4": { + "mime_guess 2.0.5": { "name": "mime_guess", - "version": "2.0.4", + "version": "2.0.5", "package_url": "https://github.com/abonander/mime_guess", "repository": { "Http": { - "url": "https://static.crates.io/crates/mime_guess/2.0.4/download", - "sha256": "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" + "url": "https://static.crates.io/crates/mime_guess/2.0.5/download", + "sha256": "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" } }, "targets": [ @@ -48591,7 +48743,7 @@ "target": "mime" }, { - "id": "mime_guess 2.0.4", + "id": "mime_guess 2.0.5", "target": "build_script_build" }, { @@ -48602,7 +48754,7 @@ "selects": {} }, "edition": "2015", - "version": "2.0.4" + "version": "2.0.5" }, "build_script_attrs": { "compile_data_glob": [ @@ -48994,7 +49146,7 @@ "selects": { "cfg(target_os = \"wasi\")": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -49004,7 +49156,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -49025,14 +49177,14 @@ ], "license_file": "LICENSE" }, - "mio 1.0.2": { + "mio 1.2.0": { "name": "mio", - "version": "1.0.2", + "version": "1.2.0", "package_url": "https://github.com/tokio-rs/mio", "repository": { "Http": { - "url": "https://static.crates.io/crates/mio/1.0.2/download", - "sha256": "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" + "url": "https://static.crates.io/crates/mio/1.2.0/download", + "sha256": "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" } }, "targets": [ @@ -49094,32 +49246,21 @@ "target": "log" } ], - "cfg(target_os = \"hermit\")": [ + "cfg(any(unix, target_os = \"hermit\", target_os = \"wasi\"))": [ { - "id": "hermit-abi 0.3.9", - "target": "hermit_abi", - "alias": "libc" + "id": "libc 0.2.186", + "target": "libc" } ], "cfg(target_os = \"wasi\")": [ - { - "id": "libc 0.2.177", - "target": "libc" - }, { "id": "wasi 0.11.0+wasi-snapshot-preview1", "target": "wasi" } ], - "cfg(unix)": [ - { - "id": "libc 0.2.177", - "target": "libc" - } - ], "cfg(windows)": [ { - "id": "windows-sys 0.52.0", + "id": "windows-sys 0.61.2", "target": "windows_sys" } ], @@ -49138,7 +49279,7 @@ } }, "edition": "2021", - "version": "1.0.2" + "version": "1.2.0" }, "license": "MIT", "license_ids": [ @@ -49359,7 +49500,7 @@ "target": "colored" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -49407,7 +49548,7 @@ "target": "similar" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -49489,7 +49630,7 @@ "target": "event_listener" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -49624,7 +49765,7 @@ "target": "encoding_rs" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -49828,6 +49969,53 @@ ], "license_file": "LICENSE" }, + "nanoid 0.4.0": { + "name": "nanoid", + "version": "0.4.0", + "package_url": "https://github.com/nikolay-govorov/nanoid.git", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/nanoid/0.4.0/download", + "sha256": "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" + } + }, + "targets": [ + { + "Library": { + "crate_name": "nanoid", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "nanoid", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "rand 0.8.5", + "target": "rand" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.4.0" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, "neli 0.6.4": { "name": "neli", "version": "0.6.4", @@ -49870,7 +50058,7 @@ "target": "byteorder" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -50151,7 +50339,7 @@ "target": "cfg_if" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -50218,7 +50406,7 @@ "target": "cfg_if" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -50282,7 +50470,7 @@ "target": "cfg_if" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -50380,7 +50568,7 @@ "target": "cfg_if" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -50481,7 +50669,7 @@ "target": "cfg_if" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -50611,6 +50799,51 @@ ], "license_file": "LICENSE-APACHE" }, + "nom 1.2.4": { + "name": "nom", + "version": "1.2.4", + "package_url": "https://github.com/Geal/nom", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/nom/1.2.4/download", + "sha256": "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" + } + }, + "targets": [ + { + "Library": { + "crate_name": "nom", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "nom", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "stream" + ], + "selects": {} + }, + "edition": "2015", + "version": "1.2.4" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, "nom 7.1.3": { "name": "nom", "version": "7.1.3", @@ -51988,7 +52221,7 @@ "selects": { "cfg(not(windows))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -52168,7 +52401,7 @@ "selects": { "cfg(any(target_os = \"macos\", target_os = \"ios\", target_os = \"freebsd\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -52338,7 +52571,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -53202,7 +53435,7 @@ "target": "foreign_types" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -53447,7 +53680,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -53608,11 +53841,11 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { @@ -53639,7 +53872,7 @@ "selects": { "cfg(all(target_arch = \"wasm32\", not(target_os = \"wasi\")))": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" } ] @@ -53696,11 +53929,11 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { @@ -53719,7 +53952,7 @@ "selects": { "cfg(all(target_arch = \"wasm32\", not(target_os = \"wasi\")))": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" } ] @@ -53782,7 +54015,7 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -53810,7 +54043,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -54069,11 +54302,11 @@ "deps": { "common": [ { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -54096,7 +54329,7 @@ "selects": { "cfg(target_arch = \"wasm32\")": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" } ] @@ -54158,15 +54391,15 @@ "target": "crossbeam_channel" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-executor 0.3.31", + "id": "futures-executor 0.3.32", "target": "futures_executor" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -54262,19 +54495,19 @@ "target": "crossbeam_channel" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-executor 0.3.31", + "id": "futures-executor 0.3.32", "target": "futures_executor" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { - "id": "glob 0.3.1", + "id": "glob 0.3.3", "target": "glob" }, { @@ -54302,7 +54535,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -54381,19 +54614,19 @@ "deps": { "common": [ { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-executor 0.3.31", + "id": "futures-executor 0.3.32", "target": "futures_executor" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { - "id": "glob 0.3.1", + "id": "glob 0.3.3", "target": "glob" }, { @@ -54417,7 +54650,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -54541,6 +54774,60 @@ ], "license_file": "LICENSE-MIT" }, + "ordered-float 5.3.0": { + "name": "ordered-float", + "version": "5.3.0", + "package_url": "https://github.com/reem/rust-ordered-float", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ordered-float/5.3.0/download", + "sha256": "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ordered_float", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ordered_float", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "num-traits 0.2.19", + "target": "num_traits" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "5.3.0" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE-MIT" + }, "ordered-multimap 0.7.3": { "name": "ordered-multimap", "version": "0.7.3", @@ -55326,7 +55613,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -55424,7 +55711,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -55776,7 +56063,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -55844,7 +56131,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -56121,7 +56408,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -57354,7 +57641,7 @@ "target": "fastrand" }, { - "id": "futures-io 0.3.31", + "id": "futures-io 0.3.32", "target": "futures_io" } ], @@ -57701,11 +57988,11 @@ "selects": { "cfg(all(target_arch = \"wasm32\", not(target_os = \"wasi\")))": [ { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" }, { - "id": "web-sys 0.3.64", + "id": "web-sys 0.3.97", "target": "web_sys" } ] @@ -57913,7 +58200,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -58469,7 +58756,7 @@ "target": "inferno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -60293,7 +60580,7 @@ "target": "lazy_static" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -60418,7 +60705,7 @@ "target": "lazy_static" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -62287,7 +62574,7 @@ "selects": { "cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))": [ { - "id": "web-sys 0.3.64", + "id": "web-sys 0.3.97", "target": "web_sys" } ], @@ -62299,7 +62586,7 @@ ], "cfg(not(any(target_os = \"windows\", target_arch = \"wasm32\")))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -62606,7 +62893,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -62657,6 +62944,7 @@ ], "crate_features": { "common": [ + "default", "log", "ring", "rustls" @@ -62754,7 +63042,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -63079,7 +63367,7 @@ "selects": { "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -63177,25 +63465,25 @@ "selects": { "aarch64-apple-darwin": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "aarch64-unknown-linux-gnu": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "x86_64-apple-darwin": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "x86_64-unknown-linux-gnu": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -63886,7 +64174,7 @@ "selects": { "cfg(any(target_os = \"macos\", target_os = \"ios\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -63965,7 +64253,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -66045,11 +66333,11 @@ "target": "bytes" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -66184,25 +66472,25 @@ "target": "pin_project_lite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], "cfg(target_arch = \"wasm32\")": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" }, { - "id": "wasm-bindgen-futures 0.4.37", + "id": "wasm-bindgen-futures 0.4.70", "target": "wasm_bindgen_futures" }, { - "id": "web-sys 0.3.64", + "id": "web-sys 0.3.97", "target": "web_sys" } ], @@ -66352,11 +66640,11 @@ "target": "bytes" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -66364,7 +66652,7 @@ "target": "http" }, { - "id": "mime_guess 2.0.4", + "id": "mime_guess 2.0.5", "target": "mime_guess" }, { @@ -66395,7 +66683,7 @@ "target": "async_compression" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { @@ -66445,7 +66733,7 @@ "target": "async_compression" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { @@ -66519,7 +66807,7 @@ "target": "pin_project_lite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -66527,7 +66815,7 @@ "target": "tower" }, { - "id": "tower-http 0.6.6", + "id": "tower-http 0.6.8", "target": "tower_http" }, { @@ -66537,19 +66825,19 @@ ], "cfg(target_arch = \"wasm32\")": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" }, { - "id": "wasm-bindgen-futures 0.4.37", + "id": "wasm-bindgen-futures 0.4.70", "target": "wasm_bindgen_futures" }, { - "id": "web-sys 0.3.64", + "id": "web-sys 0.3.97", "target": "web_sys" } ], @@ -66565,7 +66853,7 @@ "target": "async_compression" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { @@ -66615,7 +66903,7 @@ "target": "async_compression" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { @@ -66671,6 +66959,257 @@ ], "license_file": "LICENSE-APACHE" }, + "reqwest 0.13.3": { + "name": "reqwest", + "version": "0.13.3", + "package_url": "https://github.com/seanmonstar/reqwest", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/reqwest/0.13.3/download", + "sha256": "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" + } + }, + "targets": [ + { + "Library": { + "crate_name": "reqwest", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "reqwest", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "__rustls", + "__tls", + "charset", + "http2", + "json", + "multipart", + "rustls-no-provider", + "stream", + "system-proxy" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base64 0.22.1", + "target": "base64" + }, + { + "id": "bytes 1.11.1", + "target": "bytes" + }, + { + "id": "futures-core 0.3.32", + "target": "futures_core" + }, + { + "id": "futures-util 0.3.32", + "target": "futures_util" + }, + { + "id": "http 1.3.1", + "target": "http" + }, + { + "id": "mime_guess 2.0.5", + "target": "mime_guess" + }, + { + "id": "serde 1.0.228", + "target": "serde" + }, + { + "id": "serde_json 1.0.145", + "target": "serde_json" + }, + { + "id": "sync_wrapper 1.0.2", + "target": "sync_wrapper" + }, + { + "id": "url 2.5.4", + "target": "url" + } + ], + "selects": { + "aarch64-apple-darwin": [ + { + "id": "encoding_rs 0.8.32", + "target": "encoding_rs" + }, + { + "id": "h2 0.4.4", + "target": "h2" + }, + { + "id": "mime 0.3.17", + "target": "mime" + }, + { + "id": "tokio-util 0.7.17", + "target": "tokio_util" + } + ], + "aarch64-unknown-linux-gnu": [ + { + "id": "encoding_rs 0.8.32", + "target": "encoding_rs" + }, + { + "id": "h2 0.4.4", + "target": "h2" + }, + { + "id": "mime 0.3.17", + "target": "mime" + }, + { + "id": "tokio-util 0.7.17", + "target": "tokio_util" + } + ], + "cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))": [ + { + "id": "js-sys 0.3.97", + "target": "js_sys" + }, + { + "id": "wasm-bindgen 0.2.120", + "target": "wasm_bindgen" + }, + { + "id": "wasm-bindgen-futures 0.4.70", + "target": "wasm_bindgen_futures" + }, + { + "id": "web-sys 0.3.97", + "target": "web_sys" + } + ], + "cfg(not(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"))))": [ + { + "id": "http-body 1.0.1", + "target": "http_body" + }, + { + "id": "http-body-util 0.1.3", + "target": "http_body_util" + }, + { + "id": "hyper 1.8.1", + "target": "hyper" + }, + { + "id": "hyper-util 0.1.12", + "target": "hyper_util" + }, + { + "id": "log 0.4.28", + "target": "log" + }, + { + "id": "percent-encoding 2.3.1", + "target": "percent_encoding" + }, + { + "id": "pin-project-lite 0.2.16", + "target": "pin_project_lite" + }, + { + "id": "tokio 1.52.1", + "target": "tokio" + }, + { + "id": "tower 0.5.2", + "target": "tower" + }, + { + "id": "tower-http 0.6.8", + "target": "tower_http" + }, + { + "id": "tower-service 0.3.3", + "target": "tower_service" + } + ], + "wasm32-unknown-unknown": [ + { + "id": "wasm-streams 0.5.0", + "target": "wasm_streams" + } + ], + "x86_64-apple-darwin": [ + { + "id": "encoding_rs 0.8.32", + "target": "encoding_rs" + }, + { + "id": "h2 0.4.4", + "target": "h2" + }, + { + "id": "mime 0.3.17", + "target": "mime" + }, + { + "id": "tokio-util 0.7.17", + "target": "tokio_util" + } + ], + "x86_64-unknown-linux-gnu": [ + { + "id": "encoding_rs 0.8.32", + "target": "encoding_rs" + }, + { + "id": "h2 0.4.4", + "target": "h2" + }, + { + "id": "mime 0.3.17", + "target": "mime" + }, + { + "id": "tokio-util 0.7.17", + "target": "tokio_util" + } + ] + } + }, + "extra_deps": { + "common": [ + "@crate_index__hyper-rustls-0.27.7//:hyper_rustls", + "@crate_index__rustls-0.23.27//:rustls", + "@crate_index__rustls-pki-types-1.12.0//:rustls_pki_types", + "@crate_index__rustls-platform-verifier-0.6.2//:rustls_platform_verifier", + "@crate_index__tokio-rustls-0.26.0//:tokio_rustls" + ], + "selects": {} + }, + "edition": "2021", + "version": "0.13.3" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "resolv-conf 0.7.0": { "name": "resolv-conf", "version": "0.7.0", @@ -67001,6 +67540,151 @@ ], "license_file": "LICENSE" }, + "rig-core 0.36.0": { + "name": "rig-core", + "version": "0.36.0", + "package_url": "https://github.com/0xPlaygrounds/rig", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/rig-core/0.36.0/download", + "sha256": "9ac7d75266ac1f79b1faeff611c85cb40be00a0a983db10036591424f0433dc1" + } + }, + "targets": [ + { + "Library": { + "crate_name": "rig", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "rig", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "reqwest" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "as-any 0.3.2", + "target": "as_any" + }, + { + "id": "async-stream 0.3.6", + "target": "async_stream" + }, + { + "id": "base64 0.22.1", + "target": "base64" + }, + { + "id": "bytes 1.11.1", + "target": "bytes" + }, + { + "id": "eventsource-stream 0.2.3", + "target": "eventsource_stream" + }, + { + "id": "fastrand 2.3.0", + "target": "fastrand" + }, + { + "id": "futures 0.3.32", + "target": "futures" + }, + { + "id": "futures-timer 3.0.3", + "target": "futures_timer" + }, + { + "id": "glob 0.3.3", + "target": "glob" + }, + { + "id": "http 1.3.1", + "target": "http" + }, + { + "id": "mime 0.3.17", + "target": "mime" + }, + { + "id": "mime_guess 2.0.5", + "target": "mime_guess" + }, + { + "id": "nanoid 0.4.0", + "target": "nanoid" + }, + { + "id": "ordered-float 5.3.0", + "target": "ordered_float" + }, + { + "id": "pin-project-lite 0.2.16", + "target": "pin_project_lite" + }, + { + "id": "reqwest 0.13.3", + "target": "reqwest" + }, + { + "id": "schemars 1.1.0", + "target": "schemars" + }, + { + "id": "serde 1.0.228", + "target": "serde" + }, + { + "id": "serde_json 1.0.145", + "target": "serde_json" + }, + { + "id": "thiserror 2.0.18", + "target": "thiserror" + }, + { + "id": "tokio 1.52.1", + "target": "tokio" + }, + { + "id": "tracing 0.1.41", + "target": "tracing" + }, + { + "id": "tracing-futures 0.2.5", + "target": "tracing_futures" + }, + { + "id": "url 2.5.4", + "target": "url" + } + ], + "selects": {} + }, + "edition": "2024", + "version": "0.36.0" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, "ring 0.16.20": { "name": "ring", "version": "0.16.20", @@ -67071,7 +67755,7 @@ ], "cfg(all(target_arch = \"wasm32\", target_vendor = \"unknown\", target_os = \"unknown\", target_env = \"\"))": [ { - "id": "web-sys 0.3.64", + "id": "web-sys 0.3.97", "target": "web_sys" } ], @@ -67083,7 +67767,7 @@ ], "cfg(any(target_os = \"android\", target_os = \"linux\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -67219,13 +67903,13 @@ ], "cfg(all(all(target_arch = \"aarch64\", target_endian = \"little\"), target_vendor = \"apple\", any(target_os = \"ios\", target_os = \"macos\", target_os = \"tvos\", target_os = \"visionos\", target_os = \"watchos\")))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(all(any(all(target_arch = \"aarch64\", target_endian = \"little\"), all(target_arch = \"arm\", target_endian = \"little\")), any(target_os = \"android\", target_os = \"linux\")))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -67785,7 +68469,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -67999,7 +68683,7 @@ "deps": { "common": [ { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -68082,7 +68766,7 @@ "target": "cfg_if" }, { - "id": "glob 0.3.1", + "id": "glob 0.3.3", "target": "glob" }, { @@ -68190,7 +68874,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -68889,7 +69573,7 @@ "alias": "libc_errno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -68912,7 +69596,7 @@ "alias": "libc_errno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -68934,7 +69618,7 @@ "alias": "libc_errno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -68945,7 +69629,7 @@ "alias": "libc_errno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -69062,7 +69746,7 @@ "alias": "libc_errno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -69085,7 +69769,7 @@ "alias": "libc_errno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -69107,7 +69791,7 @@ "alias": "libc_errno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -69118,7 +69802,7 @@ "alias": "libc_errno" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -69568,7 +70252,7 @@ "target": "chrono" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -70947,7 +71631,84 @@ "selects": {} }, "edition": "2021", - "version": "0.9.0" + "version": "0.9.0" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, + "schemars 1.1.0": { + "name": "schemars", + "version": "1.1.0", + "package_url": "https://github.com/GREsau/schemars", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/schemars/1.1.0/download", + "sha256": "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" + } + }, + "targets": [ + { + "Library": { + "crate_name": "schemars", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "schemars", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "derive", + "schemars_derive", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "dyn-clone 1.0.20", + "target": "dyn_clone" + }, + { + "id": "ref-cast 1.0.25", + "target": "ref_cast" + }, + { + "id": "serde 1.0.228", + "target": "serde" + }, + { + "id": "serde_json 1.0.145", + "target": "serde_json" + } + ], + "selects": {} + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "schemars_derive 1.1.0", + "target": "schemars_derive" + } + ], + "selects": {} + }, + "version": "1.1.0" }, "license": "MIT", "license_ids": [ @@ -70955,20 +71716,20 @@ ], "license_file": "LICENSE" }, - "schemars 1.1.0": { - "name": "schemars", - "version": "1.1.0", + "schemars_derive 0.8.21": { + "name": "schemars_derive", + "version": "0.8.21", "package_url": "https://github.com/GREsau/schemars", "repository": { "Http": { - "url": "https://static.crates.io/crates/schemars/1.1.0/download", - "sha256": "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" + "url": "https://static.crates.io/crates/schemars_derive/0.8.21/download", + "sha256": "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" } }, "targets": [ { - "Library": { - "crate_name": "schemars", + "ProcMacro": { + "crate_name": "schemars_derive", "crate_root": "src/lib.rs", "srcs": { "allow_empty": true, @@ -70979,7 +71740,7 @@ } } ], - "library_target_name": "schemars", + "library_target_name": "schemars_derive", "common_attrs": { "compile_data_glob": [ "**" @@ -70987,26 +71748,26 @@ "deps": { "common": [ { - "id": "dyn-clone 1.0.20", - "target": "dyn_clone" + "id": "proc-macro2 1.0.103", + "target": "proc_macro2" }, { - "id": "ref-cast 1.0.25", - "target": "ref_cast" + "id": "quote 1.0.42", + "target": "quote" }, { - "id": "serde 1.0.228", - "target": "serde" + "id": "serde_derive_internals 0.29.1", + "target": "serde_derive_internals" }, { - "id": "serde_json 1.0.145", - "target": "serde_json" + "id": "syn 2.0.110", + "target": "syn" } ], "selects": {} }, "edition": "2021", - "version": "1.1.0" + "version": "0.8.21" }, "license": "MIT", "license_ids": [ @@ -71014,14 +71775,14 @@ ], "license_file": "LICENSE" }, - "schemars_derive 0.8.21": { + "schemars_derive 1.1.0": { "name": "schemars_derive", - "version": "0.8.21", + "version": "1.1.0", "package_url": "https://github.com/GREsau/schemars", "repository": { "Http": { - "url": "https://static.crates.io/crates/schemars_derive/0.8.21/download", - "sha256": "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" + "url": "https://static.crates.io/crates/schemars_derive/1.1.0/download", + "sha256": "301858a4023d78debd2353c7426dc486001bddc91ae31a76fb1f55132f7e2633" } }, "targets": [ @@ -71065,7 +71826,7 @@ "selects": {} }, "edition": "2021", - "version": "0.8.21" + "version": "1.1.0" }, "license": "MIT", "license_ids": [ @@ -72029,7 +72790,7 @@ "target": "core_foundation_sys" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -72100,7 +72861,7 @@ "target": "core_foundation_sys" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -72166,7 +72927,7 @@ "target": "core_foundation_sys" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -72545,7 +73306,7 @@ "deps": { "common": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" }, { @@ -72553,7 +73314,7 @@ "target": "serde" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ], @@ -73973,7 +74734,7 @@ "target": "lazy_static" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -74307,7 +75068,7 @@ "target": "sha2" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -74621,7 +75382,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -74697,7 +75458,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -74706,7 +75467,7 @@ "alias": "mio_0_8" }, { - "id": "mio 1.0.2", + "id": "mio 1.2.0", "target": "mio", "alias": "mio_1_0" }, @@ -74759,7 +75520,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -76511,7 +77272,7 @@ "selects": { "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -76573,7 +77334,7 @@ "selects": { "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -76595,14 +77356,14 @@ ], "license_file": "LICENSE-APACHE" }, - "socket2 0.6.1": { + "socket2 0.6.3": { "name": "socket2", - "version": "0.6.1", + "version": "0.6.3", "package_url": "https://github.com/rust-lang/socket2", "repository": { "Http": { - "url": "https://static.crates.io/crates/socket2/0.6.1/download", - "sha256": "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" + "url": "https://static.crates.io/crates/socket2/0.6.3/download", + "sha256": "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" } }, "targets": [ @@ -76633,22 +77394,22 @@ "deps": { "common": [], "selects": { - "cfg(unix)": [ + "cfg(any(unix, target_os = \"wasi\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], "cfg(windows)": [ { - "id": "windows-sys 0.60.2", + "id": "windows-sys 0.61.2", "target": "windows_sys" } ] } }, "edition": "2021", - "version": "0.6.1" + "version": "0.6.3" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -76715,7 +77476,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -77023,7 +77784,7 @@ "target": "bitflags" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -77142,7 +77903,7 @@ "target": "cfg_if" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -77272,7 +78033,7 @@ "target": "cfg_if" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -78114,7 +78875,7 @@ "target": "rand" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -78618,7 +79379,7 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" } ], @@ -78739,7 +79500,7 @@ "target": "bitflags" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -78936,7 +79697,7 @@ ], "cfg(not(any(target_os = \"unknown\", target_arch = \"wasm32\")))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -79017,6 +79778,62 @@ ], "license_file": null }, + "system-configuration 0.6.1": { + "name": "system-configuration", + "version": "0.6.1", + "package_url": "https://github.com/mullvad/system-configuration-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/system-configuration/0.6.1/download", + "sha256": "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "system_configuration", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "system_configuration", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "bitflags 2.10.0", + "target": "bitflags" + }, + { + "id": "core-foundation 0.9.4", + "target": "core_foundation" + }, + { + "id": "system-configuration-sys 0.6.0", + "target": "system_configuration_sys" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.6.1" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "system-configuration-sys 0.5.0": { "name": "system-configuration-sys", "version": "0.5.0", @@ -79065,7 +79882,7 @@ "target": "core_foundation_sys" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -79096,6 +79913,85 @@ ], "license_file": null }, + "system-configuration-sys 0.6.0": { + "name": "system-configuration-sys", + "version": "0.6.0", + "package_url": "https://github.com/mullvad/system-configuration-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/system-configuration-sys/0.6.0/download", + "sha256": "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" + } + }, + "targets": [ + { + "Library": { + "crate_name": "system_configuration_sys", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "system_configuration_sys", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "core-foundation-sys 0.8.7", + "target": "core_foundation_sys" + }, + { + "id": "libc 0.2.186", + "target": "libc" + }, + { + "id": "system-configuration-sys 0.6.0", + "target": "build_script_build" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.6.0" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "compile_data_glob_excludes": [ + "**/*.rs" + ], + "data_glob": [ + "**" + ] + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "systemd 0.10.0": { "name": "systemd", "version": "0.10.0", @@ -79144,7 +80040,7 @@ "target": "foreign_types" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -79215,7 +80111,7 @@ "target": "lazy_static" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -79555,7 +80451,7 @@ ], "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -79715,7 +80611,7 @@ "target": "fnv" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -79747,7 +80643,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -80278,7 +81174,7 @@ "selects": { "cfg(not(windows))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -80332,7 +81228,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -80481,7 +81377,7 @@ "target": "getopts" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -81100,7 +81996,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -81181,7 +82077,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -81277,7 +82173,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -81370,7 +82266,7 @@ "selects": { "aarch64-apple-darwin": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -81380,7 +82276,7 @@ ], "aarch64-unknown-linux-gnu": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -81390,7 +82286,7 @@ ], "x86_64-apple-darwin": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -81400,7 +82296,7 @@ ], "x86_64-unknown-linux-gnu": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -81616,7 +82512,7 @@ "selects": { "cfg(target_arch = \"wasm32\")": [ { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ] @@ -82046,14 +82942,14 @@ ], "license_file": null }, - "tokio 1.48.0": { + "tokio 1.52.1": { "name": "tokio", - "version": "1.48.0", + "version": "1.52.1", "package_url": "https://github.com/tokio-rs/tokio", "repository": { "Http": { - "url": "https://static.crates.io/crates/tokio/1.48.0/download", - "sha256": "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" + "url": "https://static.crates.io/crates/tokio/1.52.1/download", + "sha256": "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" } }, "targets": [ @@ -82109,7 +83005,7 @@ "target": "bytes" }, { - "id": "mio 1.0.2", + "id": "mio 1.2.0", "target": "mio" }, { @@ -82124,7 +83020,7 @@ "selects": { "aarch64-apple-darwin": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -82132,13 +83028,13 @@ "target": "signal_hook_registry" }, { - "id": "socket2 0.6.1", + "id": "socket2 0.6.3", "target": "socket2" } ], "aarch64-unknown-linux-gnu": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -82146,13 +83042,13 @@ "target": "signal_hook_registry" }, { - "id": "socket2 0.6.1", + "id": "socket2 0.6.3", "target": "socket2" } ], "x86_64-apple-darwin": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -82160,13 +83056,13 @@ "target": "signal_hook_registry" }, { - "id": "socket2 0.6.1", + "id": "socket2 0.6.3", "target": "socket2" } ], "x86_64-unknown-linux-gnu": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -82174,7 +83070,7 @@ "target": "signal_hook_registry" }, { - "id": "socket2 0.6.1", + "id": "socket2 0.6.3", "target": "socket2" } ] @@ -82184,13 +83080,13 @@ "proc_macro_deps": { "common": [ { - "id": "tokio-macros 2.6.0", + "id": "tokio-macros 2.7.0", "target": "tokio_macros" } ], "selects": {} }, - "version": "1.48.0" + "version": "1.52.1" }, "license": "MIT", "license_ids": [ @@ -82234,7 +83130,7 @@ "target": "pin_project_lite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -82250,14 +83146,14 @@ ], "license_file": "LICENSE-APACHE" }, - "tokio-macros 2.6.0": { + "tokio-macros 2.7.0": { "name": "tokio-macros", - "version": "2.6.0", + "version": "2.7.0", "package_url": "https://github.com/tokio-rs/tokio", "repository": { "Http": { - "url": "https://static.crates.io/crates/tokio-macros/2.6.0/download", - "sha256": "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" + "url": "https://static.crates.io/crates/tokio-macros/2.7.0/download", + "sha256": "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" } }, "targets": [ @@ -82297,7 +83193,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "2.7.0" }, "license": "MIT", "license_ids": [ @@ -82345,7 +83241,7 @@ "deps": { "common": [ { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -82353,7 +83249,7 @@ "target": "pin_project_lite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -82418,7 +83314,7 @@ "target": "rusqlite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -82477,7 +83373,7 @@ "target": "rustls" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -82543,7 +83439,7 @@ "alias": "pki_types" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -82609,7 +83505,7 @@ "alias": "pki_types" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -82677,11 +83573,11 @@ "target": "bytes" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { @@ -82761,7 +83657,7 @@ "target": "either" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -82769,7 +83665,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -82824,7 +83720,7 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -82832,7 +83728,7 @@ "target": "pin_project_lite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -82887,11 +83783,11 @@ "target": "bytes" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -82951,7 +83847,7 @@ "deps": { "common": [ { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -82959,7 +83855,7 @@ "target": "log" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -83019,7 +83915,7 @@ "deps": { "common": [ { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -83027,7 +83923,7 @@ "target": "log" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -83101,19 +83997,19 @@ "target": "bytes" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-io 0.3.31", + "id": "futures-io 0.3.32", "target": "futures_io" }, { - "id": "futures-sink 0.3.31", + "id": "futures-sink 0.3.32", "target": "futures_sink" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -83129,7 +84025,7 @@ "target": "slab" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" } ], @@ -83467,7 +84363,7 @@ "target": "socket2" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -83602,7 +84498,7 @@ "target": "pin_project" }, { - "id": "socket2 0.6.1", + "id": "socket2 0.6.3", "target": "socket2" }, { @@ -83610,7 +84506,7 @@ "target": "sync_wrapper" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -83789,11 +84685,11 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -83817,7 +84713,7 @@ "target": "slab" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -83914,11 +84810,11 @@ "deps": { "common": [ { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -83942,7 +84838,7 @@ "target": "sync_wrapper" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -84039,7 +84935,7 @@ "target": "pin_project_lite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -84066,14 +84962,14 @@ ], "license_file": "LICENSE" }, - "tower-http 0.6.6": { + "tower-http 0.6.8": { "name": "tower-http", - "version": "0.6.6", + "version": "0.6.8", "package_url": "https://github.com/tower-rs/tower-http", "repository": { "Http": { - "url": "https://static.crates.io/crates/tower-http/0.6.6/download", - "sha256": "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" + "url": "https://static.crates.io/crates/tower-http/0.6.8/download", + "sha256": "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" } }, "targets": [ @@ -84155,7 +85051,7 @@ "target": "bytes" }, { - "id": "futures-core 0.3.31", + "id": "futures-core 0.3.32", "target": "futures_core" }, { @@ -84175,7 +85071,7 @@ "target": "pin_project_lite" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -84206,7 +85102,7 @@ "selects": { "aarch64-apple-darwin": [ { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -84216,7 +85112,7 @@ ], "aarch64-unknown-linux-gnu": [ { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -84226,7 +85122,7 @@ ], "x86_64-apple-darwin": [ { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -84236,7 +85132,7 @@ ], "x86_64-unknown-linux-gnu": [ { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -84247,7 +85143,7 @@ } }, "edition": "2018", - "version": "0.6.6" + "version": "0.6.8" }, "license": "MIT", "license_ids": [ @@ -84422,7 +85318,7 @@ "deps": { "common": [ { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -84430,7 +85326,7 @@ "target": "pin_project" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -84933,6 +85829,77 @@ ], "license_file": "LICENSE" }, + "tracing-futures 0.2.5": { + "name": "tracing-futures", + "version": "0.2.5", + "package_url": "https://github.com/tokio-rs/tracing", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/tracing-futures/0.2.5/download", + "sha256": "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" + } + }, + "targets": [ + { + "Library": { + "crate_name": "tracing_futures", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "tracing_futures", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "futures", + "futures-03", + "futures-task", + "pin-project", + "std", + "std-future" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "futures 0.3.32", + "target": "futures" + }, + { + "id": "futures-task 0.3.32", + "target": "futures_task" + }, + { + "id": "pin-project 1.1.2", + "target": "pin_project" + }, + { + "id": "tracing 0.1.41", + "target": "tracing" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.2.5" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, "tracing-journald 0.3.2": { "name": "tracing-journald", "version": "0.3.2", @@ -84965,7 +85932,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -85189,7 +86156,7 @@ "selects": { "cfg(all(target_arch = \"wasm32\", not(target_os = \"wasi\")))": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" }, { @@ -85659,15 +86626,15 @@ "target": "data_encoding" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-io 0.3.31", + "id": "futures-io 0.3.32", "target": "futures_io" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -85699,7 +86666,7 @@ "target": "tinyvec" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -85783,7 +86750,7 @@ "target": "cfg_if" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -85811,7 +86778,7 @@ "target": "thiserror" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -86167,7 +87134,7 @@ "target": "bytes" }, { - "id": "futures 0.3.31", + "id": "futures 0.3.32", "target": "futures" }, { @@ -86187,7 +87154,7 @@ "target": "scoped_tls" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -87980,7 +88947,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -88047,7 +89014,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -88322,7 +89289,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -88375,7 +89342,7 @@ "selects": { "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -88863,11 +89830,11 @@ "target": "bytes" }, { - "id": "futures-channel 0.3.31", + "id": "futures-channel 0.3.32", "target": "futures_channel" }, { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { @@ -88891,7 +89858,7 @@ "target": "mime" }, { - "id": "mime_guess 2.0.4", + "id": "mime_guess 2.0.5", "target": "mime_guess" }, { @@ -88927,7 +89894,7 @@ "target": "serde_urlencoded" }, { - "id": "tokio 1.48.0", + "id": "tokio 1.52.1", "target": "tokio" }, { @@ -89049,14 +90016,14 @@ ], "license_file": "LICENSE-APACHE" }, - "wasm-bindgen 0.2.100": { + "wasm-bindgen 0.2.120": { "name": "wasm-bindgen", - "version": "0.2.100", - "package_url": "https://github.com/rustwasm/wasm-bindgen", + "version": "0.2.120", + "package_url": "https://github.com/wasm-bindgen/wasm-bindgen", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasm-bindgen/0.2.100/download", - "sha256": "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" + "url": "https://static.crates.io/crates/wasm-bindgen/0.2.120/download", + "sha256": "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" } }, "targets": [ @@ -89093,8 +90060,6 @@ "crate_features": { "common": [ "default", - "msrv", - "rustversion", "std" ], "selects": {} @@ -89110,8 +90075,12 @@ "target": "once_cell" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "build_script_build" + }, + { + "id": "wasm-bindgen-shared 0.2.120", + "target": "wasm_bindgen_shared" } ], "selects": {} @@ -89120,17 +90089,13 @@ "proc_macro_deps": { "common": [ { - "id": "rustversion 1.0.14", - "target": "rustversion" - }, - { - "id": "wasm-bindgen-macro 0.2.100", + "id": "wasm-bindgen-macro 0.2.120", "target": "wasm_bindgen_macro" } ], "selects": {} }, - "version": "0.2.100" + "version": "0.2.120" }, "build_script_attrs": { "compile_data_glob": [ @@ -89141,75 +90106,26 @@ ], "data_glob": [ "**" - ] - }, - "license": "MIT OR Apache-2.0", - "license_ids": [ - "Apache-2.0", - "MIT" - ], - "license_file": "LICENSE-APACHE" - }, - "wasm-bindgen-backend 0.2.100": { - "name": "wasm-bindgen-backend", - "version": "0.2.100", - "package_url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/wasm-bindgen-backend/0.2.100/download", - "sha256": "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" - } - }, - "targets": [ - { - "Library": { - "crate_name": "wasm_bindgen_backend", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "wasm_bindgen_backend", - "common_attrs": { - "compile_data_glob": [ - "**" ], - "deps": { + "link_deps": { "common": [ { - "id": "bumpalo 3.20.2", - "target": "bumpalo" - }, - { - "id": "log 0.4.28", - "target": "log" - }, - { - "id": "proc-macro2 1.0.103", - "target": "proc_macro2" - }, - { - "id": "quote 1.0.42", - "target": "quote" - }, - { - "id": "syn 2.0.110", - "target": "syn" - }, - { - "id": "wasm-bindgen-shared 0.2.100", + "id": "wasm-bindgen-shared 0.2.120", "target": "wasm_bindgen_shared" } ], "selects": {} }, - "edition": "2021", - "version": "0.2.100" + "proc_macro_deps": { + "common": [ + { + "id": "rustversion 1.0.14", + "target": "rustversion", + "alias": "rustversion_compat" + } + ], + "selects": {} + } }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -89218,14 +90134,14 @@ ], "license_file": "LICENSE-APACHE" }, - "wasm-bindgen-futures 0.4.37": { + "wasm-bindgen-futures 0.4.70": { "name": "wasm-bindgen-futures", - "version": "0.4.37", - "package_url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures", + "version": "0.4.70", + "package_url": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/futures", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasm-bindgen-futures/0.4.37/download", - "sha256": "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" + "url": "https://static.crates.io/crates/wasm-bindgen-futures/0.4.70/download", + "sha256": "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" } }, "targets": [ @@ -89247,48 +90163,44 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "default", + "std" + ], + "selects": {} + }, "deps": { "common": [ { - "id": "cfg-if 1.0.0", - "target": "cfg_if" - }, - { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ], - "selects": { - "cfg(target_feature = \"atomics\")": [ - { - "id": "web-sys 0.3.64", - "target": "web_sys" - } - ] - } + "selects": {} }, - "edition": "2018", - "version": "0.4.37" + "edition": "2021", + "version": "0.4.70" }, - "license": "MIT/Apache-2.0", + "license": "MIT OR Apache-2.0", "license_ids": [ "Apache-2.0", "MIT" ], "license_file": "LICENSE-APACHE" }, - "wasm-bindgen-macro 0.2.100": { + "wasm-bindgen-macro 0.2.120": { "name": "wasm-bindgen-macro", - "version": "0.2.100", - "package_url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro", + "version": "0.2.120", + "package_url": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/macro", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasm-bindgen-macro/0.2.100/download", - "sha256": "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" + "url": "https://static.crates.io/crates/wasm-bindgen-macro/0.2.120/download", + "sha256": "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" } }, "targets": [ @@ -89317,14 +90229,14 @@ "target": "quote" }, { - "id": "wasm-bindgen-macro-support 0.2.100", + "id": "wasm-bindgen-macro-support 0.2.120", "target": "wasm_bindgen_macro_support" } ], "selects": {} }, "edition": "2021", - "version": "0.2.100" + "version": "0.2.120" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -89333,14 +90245,14 @@ ], "license_file": "LICENSE-APACHE" }, - "wasm-bindgen-macro-support 0.2.100": { + "wasm-bindgen-macro-support 0.2.120": { "name": "wasm-bindgen-macro-support", - "version": "0.2.100", - "package_url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro-support", + "version": "0.2.120", + "package_url": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/macro-support", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasm-bindgen-macro-support/0.2.100/download", - "sha256": "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" + "url": "https://static.crates.io/crates/wasm-bindgen-macro-support/0.2.120/download", + "sha256": "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" } }, "targets": [ @@ -89364,6 +90276,10 @@ ], "deps": { "common": [ + { + "id": "bumpalo 3.20.2", + "target": "bumpalo" + }, { "id": "proc-macro2 1.0.103", "target": "proc_macro2" @@ -89377,18 +90293,14 @@ "target": "syn" }, { - "id": "wasm-bindgen-backend 0.2.100", - "target": "wasm_bindgen_backend" - }, - { - "id": "wasm-bindgen-shared 0.2.100", + "id": "wasm-bindgen-shared 0.2.120", "target": "wasm_bindgen_shared" } ], "selects": {} }, "edition": "2021", - "version": "0.2.100" + "version": "0.2.120" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -89397,14 +90309,14 @@ ], "license_file": "LICENSE-APACHE" }, - "wasm-bindgen-shared 0.2.100": { + "wasm-bindgen-shared 0.2.120": { "name": "wasm-bindgen-shared", - "version": "0.2.100", - "package_url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/shared", + "version": "0.2.120", + "package_url": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/shared", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasm-bindgen-shared/0.2.100/download", - "sha256": "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" + "url": "https://static.crates.io/crates/wasm-bindgen-shared/0.2.120/download", + "sha256": "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" } }, "targets": [ @@ -89445,14 +90357,14 @@ "target": "unicode_ident" }, { - "id": "wasm-bindgen-shared 0.2.100", + "id": "wasm-bindgen-shared 0.2.120", "target": "build_script_build" } ], "selects": {} }, "edition": "2021", - "version": "0.2.100" + "version": "0.2.120" }, "build_script_attrs": { "compile_data_glob": [ @@ -89873,23 +90785,23 @@ "deps": { "common": [ { - "id": "futures-util 0.3.31", + "id": "futures-util 0.3.32", "target": "futures_util" }, { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" }, { - "id": "wasm-bindgen-futures 0.4.37", + "id": "wasm-bindgen-futures 0.4.70", "target": "wasm_bindgen_futures" }, { - "id": "web-sys 0.3.64", + "id": "web-sys 0.3.97", "target": "web_sys" } ], @@ -89905,6 +90817,70 @@ ], "license_file": "LICENSE-APACHE" }, + "wasm-streams 0.5.0": { + "name": "wasm-streams", + "version": "0.5.0", + "package_url": "https://github.com/MattiasBuelens/wasm-streams/", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/wasm-streams/0.5.0/download", + "sha256": "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" + } + }, + "targets": [ + { + "Library": { + "crate_name": "wasm_streams", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "wasm_streams", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "futures-util 0.3.32", + "target": "futures_util" + }, + { + "id": "js-sys 0.3.97", + "target": "js_sys" + }, + { + "id": "wasm-bindgen 0.2.120", + "target": "wasm_bindgen" + }, + { + "id": "wasm-bindgen-futures 0.4.70", + "target": "wasm_bindgen_futures" + }, + { + "id": "web-sys 0.3.97", + "target": "web_sys" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.5.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "wasmparser 0.212.0": { "name": "wasmparser", "version": "0.212.0", @@ -90523,7 +91499,7 @@ "target": "cfg_if" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -91137,7 +92113,7 @@ "selects": { "cfg(unix)": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -91345,7 +92321,7 @@ "selects": { "cfg(any(target_os = \"linux\", target_vendor = \"apple\", target_os = \"freebsd\", target_os = \"android\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -91796,14 +92772,14 @@ ], "license_file": null }, - "web-sys 0.3.64": { + "web-sys 0.3.97": { "name": "web-sys", - "version": "0.3.64", - "package_url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/web-sys", + "version": "0.3.97", + "package_url": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/web-sys", "repository": { "Http": { - "url": "https://static.crates.io/crates/web-sys/0.3.64/download", - "sha256": "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" + "url": "https://static.crates.io/crates/web-sys/0.3.97/download", + "sha256": "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" } }, "targets": [ @@ -91874,27 +92850,29 @@ "WorkerGlobalScope", "WritableStream", "WritableStreamDefaultController", - "WritableStreamDefaultWriter" + "WritableStreamDefaultWriter", + "default", + "std" ], "selects": {} }, "deps": { "common": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ], "selects": {} }, - "edition": "2018", - "version": "0.3.64" + "edition": "2021", + "version": "0.3.97" }, - "license": "MIT/Apache-2.0", + "license": "MIT OR Apache-2.0", "license_ids": [ "Apache-2.0", "MIT" @@ -91935,11 +92913,11 @@ "selects": { "cfg(all(target_family = \"wasm\", target_os = \"unknown\"))": [ { - "id": "js-sys 0.3.77", + "id": "js-sys 0.3.97", "target": "js_sys" }, { - "id": "wasm-bindgen 0.2.100", + "id": "wasm-bindgen 0.2.120", "target": "wasm_bindgen" } ] @@ -92173,7 +93151,7 @@ "target": "either" }, { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ], @@ -93205,6 +94183,62 @@ ], "license_file": "license-apache-2.0" }, + "windows-registry 0.4.0": { + "name": "windows-registry", + "version": "0.4.0", + "package_url": "https://github.com/microsoft/windows-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/windows-registry/0.4.0/download", + "sha256": "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" + } + }, + "targets": [ + { + "Library": { + "crate_name": "windows_registry", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "windows_registry", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "windows-result 0.3.4", + "target": "windows_result" + }, + { + "id": "windows-strings 0.3.1", + "target": "windows_strings" + }, + { + "id": "windows-targets 0.53.5", + "target": "windows_targets" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "license-apache-2.0" + }, "windows-result 0.3.4": { "name": "windows-result", "version": "0.3.4", @@ -93253,6 +94287,54 @@ ], "license_file": "license-apache-2.0" }, + "windows-strings 0.3.1": { + "name": "windows-strings", + "version": "0.3.1", + "package_url": "https://github.com/microsoft/windows-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/windows-strings/0.3.1/download", + "sha256": "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" + } + }, + "targets": [ + { + "Library": { + "crate_name": "windows_strings", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "windows_strings", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "windows-link 0.1.3", + "target": "windows_link" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.3.1" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "license-apache-2.0" + }, "windows-strings 0.4.2": { "name": "windows-strings", "version": "0.4.2", @@ -93495,54 +94577,6 @@ ], "license_file": "license-apache-2.0" }, - "windows-sys 0.60.2": { - "name": "windows-sys", - "version": "0.60.2", - "package_url": "https://github.com/microsoft/windows-rs", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/windows-sys/0.60.2/download", - "sha256": "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" - } - }, - "targets": [ - { - "Library": { - "crate_name": "windows_sys", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "windows_sys", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "deps": { - "common": [ - { - "id": "windows-targets 0.53.5", - "target": "windows_targets" - } - ], - "selects": {} - }, - "edition": "2021", - "version": "0.60.2" - }, - "license": "MIT OR Apache-2.0", - "license_ids": [ - "Apache-2.0", - "MIT" - ], - "license_file": "license-apache-2.0" - }, "windows-sys 0.61.2": { "name": "windows-sys", "version": "0.61.2", @@ -97156,7 +98190,7 @@ ], "cfg(any(target_os = \"freebsd\", target_os = \"netbsd\"))": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" } ] @@ -98305,7 +99339,7 @@ "deps": { "common": [ { - "id": "libc 0.2.177", + "id": "libc 0.2.186", "target": "libc" }, { @@ -98607,6 +99641,9 @@ "cfg(all(target_arch = \"aarch64\", target_vendor = \"apple\"))": [ "aarch64-apple-darwin" ], + "cfg(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\")))": [ + "wasm32-unknown-unknown" + ], "cfg(all(target_arch = \"wasm32\", not(target_os = \"wasi\")))": [ "wasm32-unknown-unknown" ], @@ -98712,6 +99749,12 @@ "x86_64-apple-darwin", "x86_64-unknown-linux-gnu" ], + "cfg(any(unix, target_os = \"hermit\", target_os = \"wasi\"))": [ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu" + ], "cfg(any(unix, target_os = \"redox\"))": [ "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", @@ -98732,6 +99775,12 @@ "x86_64-apple-darwin", "x86_64-unknown-linux-gnu" ], + "cfg(not(all(target_arch = \"wasm32\", any(target_os = \"unknown\", target_os = \"none\"))))": [ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu" + ], "cfg(not(any(target_os = \"unknown\", target_arch = \"wasm32\")))": [ "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", @@ -98791,7 +99840,6 @@ ], "cfg(target_env = \"msvc\")": [], "cfg(target_env = \"sgx\")": [], - "cfg(target_feature = \"atomics\")": [], "cfg(target_os = \"android\")": [], "cfg(target_os = \"cloudabi\")": [], "cfg(target_os = \"dragonfly\")": [], @@ -98931,8 +99979,8 @@ "form_urlencoded 1.2.1", "fqdn 0.5.2", "fs_extra 1.3.0", - "futures 0.3.31", - "futures-util 0.3.31", + "futures 0.3.32", + "futures-util 0.3.32", "getifs 0.4.0", "getrandom 0.2.10", "goldenfile 1.8.0", @@ -99010,7 +100058,7 @@ "k256 0.13.4", "lazy_static 1.5.0", "leb128 0.2.5", - "libc 0.2.177", + "libc 0.2.186", "libcryptsetup-rs 0.15.0", "libflate 2.1.0", "libfuzzer-sys 0.4.7", @@ -99028,6 +100076,7 @@ "memchr 2.7.4", "memmap2 0.9.5", "metrics-proxy 0.1.0", + "meval 0.2.0", "minicbor 0.19.1", "minicbor-derive 0.13.0", "mockall 0.13.1", @@ -99077,6 +100126,7 @@ "qrcode 0.14.1", "quickcheck 1.0.3", "quinn 0.11.5", + "quinn-proto 0.11.7", "quinn-udp 0.5.5", "quote 1.0.42", "rand 0.8.5", @@ -99092,6 +100142,7 @@ "reqwest 0.12.24", "rexpect 0.6.2", "rgb 0.8.37", + "rig-core 0.36.0", "ring 0.17.14", "ripemd 0.1.3", "rlp 0.5.2", @@ -99167,7 +100218,7 @@ "tikv-jemalloc-ctl 0.6.0", "tikv-jemallocator 0.6.0", "time 0.3.47", - "tokio 1.48.0", + "tokio 1.52.1", "tokio-io-timeout 1.2.0", "tokio-metrics 0.4.0", "tokio-rusqlite 0.7.0", @@ -99182,7 +100233,7 @@ "tonic 0.12.3", "tonic-build 0.12.3", "tower 0.5.2", - "tower-http 0.6.6", + "tower-http 0.6.8", "tower-request-id 0.3.0", "tower-test 0.4.0", "tower_governor 0.7.0", @@ -99204,7 +100255,7 @@ "walkdir 2.5.0", "walrus 0.23.3", "warp 0.3.7", - "wasm-bindgen 0.2.100", + "wasm-bindgen 0.2.120", "wasm-encoder 0.245.1", "wasm-smith 0.245.1", "wasmparser 0.245.1", diff --git a/Cargo.Bazel.toml.lock b/Cargo.Bazel.toml.lock index 3287600e3c3b..595d748766c1 100644 --- a/Cargo.Bazel.toml.lock +++ b/Cargo.Bazel.toml.lock @@ -2991,7 +2991,7 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags 2.10.0", "crossterm_winapi", - "mio 1.0.2", + "mio 1.2.0", "parking_lot 0.12.5", "rustix 0.38.44", "signal-hook", @@ -3801,6 +3801,7 @@ dependencies = [ "memchr", "memmap2 0.9.5", "metrics-proxy", + "meval", "minicbor", "minicbor-derive", "mockall", @@ -3850,6 +3851,7 @@ dependencies = [ "qrcode", "quickcheck", "quinn", + "quinn-proto", "quinn-udp", "quote", "rand 0.8.5", @@ -3865,6 +3867,7 @@ dependencies = [ "reqwest 0.12.24", "rexpect", "rgb", + "rig-core", "ring 0.17.14", "ripemd", "rlp", @@ -3955,7 +3958,7 @@ dependencies = [ "tonic 0.12.3", "tonic-build", "tower 0.5.2", - "tower-http 0.6.6", + "tower-http 0.6.8", "tower-request-id", "tower-test", "tower_governor 0.7.0", @@ -4598,6 +4601,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom 7.1.3", + "pin-project-lite", +] + [[package]] name = "evm_rpc_client" version = "0.4.0" @@ -4962,9 +4976,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -4977,9 +4991,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -4987,15 +5001,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -5004,9 +5018,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -5025,9 +5039,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -5047,15 +5061,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-timer" @@ -5065,9 +5079,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -5077,7 +5091,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -5200,9 +5213,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "goldenfile" @@ -5924,9 +5937,11 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2 0.5.9", + "system-configuration 0.6.1", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -6064,7 +6079,7 @@ dependencies = [ "serde_yaml_ng", "sha1", "sha2 0.10.9", - "socket2 0.6.1", + "socket2 0.6.3", "strum 0.27.2", "strum_macros 0.27.1", "systemstat", @@ -6116,7 +6131,7 @@ dependencies = [ "reqwest 0.12.24", "rustls 0.23.27", "serde", - "socket2 0.6.1", + "socket2 0.6.3", "strum 0.27.2", "thiserror 2.0.18", "tokio-util", @@ -6314,7 +6329,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-util", - "tower-http 0.6.6", + "tower-http 0.6.8", "tracing", "tracing-subscriber", ] @@ -6475,7 +6490,7 @@ dependencies = [ "tokio", "tokio-util", "tower 0.5.2", - "tower-http 0.6.6", + "tower-http 0.6.8", "tracing", "tracing-core", "tracing-serde 0.2.0", @@ -7422,10 +7437,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" dependencies = [ + "cfg-if 1.0.0", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -7731,9 +7748,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libcryptsetup-rs" @@ -8370,6 +8387,16 @@ dependencies = [ "url", ] +[[package]] +name = "meval" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79496a5651c8d57cd033c5add8ca7ee4e3d5f7587a4777484640d9cb60392d9" +dependencies = [ + "fnv", + "nom 1.2.4", +] + [[package]] name = "mime" version = "0.3.17" @@ -8378,9 +8405,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -8445,15 +8472,14 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ - "hermit-abi 0.3.9", "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8576,6 +8602,15 @@ dependencies = [ "syn 2.0.110", ] +[[package]] +name = "nanoid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" +dependencies = [ + "rand 0.8.5", +] + [[package]] name = "neli" version = "0.6.4" @@ -8692,6 +8727,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca" +[[package]] +name = "nom" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" + [[package]] name = "nom" version = "7.1.3" @@ -9265,7 +9306,7 @@ dependencies = [ "glob", "once_cell", "opentelemetry 0.21.0", - "ordered-float", + "ordered-float 4.2.0", "percent-encoding", "rand 0.8.5", "thiserror 1.0.68", @@ -9309,6 +9350,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-multimap" version = "0.7.3" @@ -11219,7 +11269,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper 0.1.2", - "system-configuration", + "system-configuration 0.5.1", "tokio", "tokio-rustls 0.24.1", "tokio-util", @@ -11270,16 +11320,55 @@ dependencies = [ "tokio-rustls 0.26.0", "tokio-util", "tower 0.5.2", - "tower-http 0.6.6", + "tower-http 0.6.8", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.4.0", "web-sys", "webpki-roots 1.0.6", ] +[[package]] +name = "reqwest" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.4", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-rustls 0.27.7", + "hyper-util", + "js-sys", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper 1.0.2", + "tokio", + "tokio-util", + "tower 0.5.2", + "tower-http 0.6.8", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams 0.5.0", + "web-sys", +] + [[package]] name = "resolv-conf" version = "0.7.0" @@ -11339,6 +11428,38 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "rig-core" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac7d75266ac1f79b1faeff611c85cb40be00a0a983db10036591424f0433dc1" +dependencies = [ + "as-any", + "async-stream", + "base64 0.22.1", + "bytes", + "eventsource-stream", + "fastrand 2.3.0", + "futures", + "futures-timer", + "glob", + "http 1.3.1", + "mime", + "mime_guess", + "nanoid", + "ordered-float 5.3.0", + "pin-project-lite", + "reqwest 0.13.3", + "schemars 1.1.0", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", + "tracing-futures", + "url", +] + [[package]] name = "ring" version = "0.16.20" @@ -11933,7 +12054,7 @@ checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "indexmap 2.13.0", - "schemars_derive", + "schemars_derive 0.8.21", "serde", "serde_json", ] @@ -11958,6 +12079,7 @@ checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" dependencies = [ "dyn-clone", "ref-cast", + "schemars_derive 1.1.0", "serde", "serde_json", ] @@ -11974,6 +12096,18 @@ dependencies = [ "syn 2.0.110", ] +[[package]] +name = "schemars_derive" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301858a4023d78debd2353c7426dc486001bddc91ae31a76fb1f55132f7e2633" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.110", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -12587,7 +12721,7 @@ checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio 0.8.10", - "mio 1.0.2", + "mio 1.2.0", "signal-hook", ] @@ -12896,12 +13030,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -13287,7 +13421,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", - "system-configuration-sys", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.9.4", + "system-configuration-sys 0.6.0", ] [[package]] @@ -13300,6 +13445,16 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "systemd" version = "0.10.0" @@ -13775,17 +13930,17 @@ dependencies = [ [[package]] name = "tokio" -version = "1.48.0" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", - "mio 1.0.2", + "mio 1.2.0", "parking_lot 0.12.5", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "socket2 0.6.3", "tokio-macros", "tracing", "windows-sys 0.61.2", @@ -13803,9 +13958,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -14046,7 +14201,7 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "socket2 0.6.1", + "socket2 0.6.3", "sync_wrapper 1.0.2", "tokio", "tokio-stream", @@ -14130,9 +14285,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", "bitflags 2.10.0", @@ -14280,6 +14435,18 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "futures", + "futures-task", + "pin-project", + "tracing", +] + [[package]] name = "tracing-journald" version = "0.3.2" @@ -14816,7 +14983,7 @@ dependencies = [ "lalrpop 0.22.1", "lz4_flex", "nom-language", - "ordered-float", + "ordered-float 4.2.0", "regex", "serde", "serde_json", @@ -14982,47 +15149,32 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" dependencies = [ "cfg-if 1.0.0", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.110", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" dependencies = [ - "cfg-if 1.0.0", "js-sys", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -15030,22 +15182,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn 2.0.110", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" dependencies = [ "unicode-ident", ] @@ -15124,6 +15276,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.212.0" @@ -15432,9 +15597,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" dependencies = [ "js-sys", "wasm-bindgen", @@ -15601,7 +15766,7 @@ dependencies = [ "windows-interface", "windows-link 0.1.3", "windows-result", - "windows-strings", + "windows-strings 0.4.2", ] [[package]] @@ -15659,6 +15824,17 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result", + "windows-strings 0.3.1", + "windows-targets 0.53.5", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -15668,6 +15844,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -15713,15 +15898,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" diff --git a/Cargo.lock b/Cargo.lock index d90fa9eed896..3b6dcbdcf532 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2469,7 +2469,7 @@ checksum = "1678b3295890df5895480a7c080430e73df2b7101f1763f62a3b32dd532f1d37" dependencies = [ "chrono", "http 1.4.0", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", "serde_urlencoded", @@ -2646,7 +2646,7 @@ dependencies = [ "network", "once_cell", "regex", - "reqwest", + "reqwest 0.12.28", "rust-ini", "securefmt", "serde", @@ -2763,7 +2763,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "registry-canister", - "reqwest", + "reqwest 0.12.28", "serde_json", "slog", "tokio", @@ -4020,7 +4020,7 @@ dependencies = [ "k256 0.11.6", "keyring", "lazy_static", - "reqwest", + "reqwest 0.12.28", "ring", "schemars 0.8.22", "sec1 0.3.0", @@ -4671,6 +4671,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom 7.1.3", + "pin-project-lite", +] + [[package]] name = "evm_rpc_client" version = "0.4.0" @@ -4779,7 +4790,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "registry-canister", - "reqwest", + "reqwest 0.12.28", "serde", "serde_cbor", "slog", @@ -6504,7 +6515,7 @@ dependencies = [ "pkcs8 0.10.2", "rand 0.8.5", "rangemap", - "reqwest", + "reqwest 0.12.28", "ring", "sec1 0.7.3", "serde", @@ -6520,6 +6531,43 @@ dependencies = [ "url", ] +[[package]] +name = "ic-ai-agent" +version = "0.9.0" +dependencies = [ + "anyhow", + "axum 0.8.8", + "chrono", + "clap 4.6.0", + "hex", + "ic-base-types", + "ic-config", + "ic-interfaces-registry", + "ic-protobuf", + "ic-registry-client", + "ic-registry-client-helpers", + "ic-registry-local-store", + "ic-state-layout", + "ic-types", + "lru 0.7.8", + "meval", + "prometheus-parse", + "reqwest 0.12.28", + "rig-core", + "rustls 0.23.37", + "serde", + "serde_json", + "slog", + "slog-async", + "slog-term", + "tempfile", + "thiserror 2.0.18", + "tokio", + "tower 0.5.3", + "tower-http", + "uuid", +] + [[package]] name = "ic-artifact-downloader" version = "0.9.0" @@ -6640,7 +6688,7 @@ dependencies = [ "ic-test-utilities-tmpdir", "ic-types", "rand 0.8.5", - "reqwest", + "reqwest 0.12.28", "rstest", "serde", "serde_json", @@ -6760,7 +6808,7 @@ dependencies = [ "rand 0.8.5", "rcgen 0.14.7", "regex", - "reqwest", + "reqwest 0.12.28", "rustls 0.23.37", "rustls-acme", "rustls-pemfile", @@ -6821,7 +6869,7 @@ dependencies = [ "parse-size", "prometheus", "rcgen 0.14.7", - "reqwest", + "reqwest 0.12.28", "rustls 0.23.37", "serde", "socket2 0.6.3", @@ -6890,7 +6938,7 @@ dependencies = [ "rate-limits-api", "ratelimit", "regex", - "reqwest", + "reqwest 0.12.28", "rustls 0.23.37", "salt-sharing-api", "serde", @@ -6928,7 +6976,7 @@ dependencies = [ "ic-crypto-tree-hash", "ic-system-test-driver", "ic-types", - "reqwest", + "reqwest 0.12.28", "serde", "serde_cbor", "slog", @@ -6952,7 +7000,7 @@ dependencies = [ "ic-system-test-driver", "prost 0.13.5", "rand 0.8.5", - "reqwest", + "reqwest 0.12.28", "slog", "tokio", ] @@ -9807,7 +9855,7 @@ dependencies = [ "prometheus", "rand 0.8.5", "regex", - "reqwest", + "reqwest 0.12.28", "rustls 0.23.37", "serde", "serde_json", @@ -9902,7 +9950,7 @@ dependencies = [ "ic-metrics", "ic-test-utilities-logger", "prometheus", - "reqwest", + "reqwest 0.12.28", "slog", "tokio", "tower 0.5.3", @@ -9976,7 +10024,7 @@ dependencies = [ "prometheus", "proptest", "prost 0.13.5", - "reqwest", + "reqwest 0.12.28", "rstest", "rustls 0.23.37", "serde", @@ -10003,7 +10051,7 @@ dependencies = [ "ic-http-endpoints-public", "ic-types", "ic-validator", - "reqwest", + "reqwest 0.12.28", "serde_cbor", "tokio", "url", @@ -10038,7 +10086,7 @@ dependencies = [ "maplit", "prometheus", "prost 0.13.5", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", "slog", @@ -10087,7 +10135,7 @@ dependencies = [ "ic-logger", "ic-test-utilities-in-memory-logger", "mockito", - "reqwest", + "reqwest 0.12.28", "slog", "tar", "tempfile", @@ -10295,7 +10343,7 @@ dependencies = [ "icrc-ledger-types", "num-bigint 0.4.6", "pocket-ic", - "reqwest", + "reqwest 0.12.28", "rosetta-core", "serde", "tempfile", @@ -10312,7 +10360,7 @@ dependencies = [ "ic-rosetta-test-utils", "icp-ledger", "pocket-ic", - "reqwest", + "reqwest 0.12.28", "tempfile", "tokio", ] @@ -10379,7 +10427,7 @@ dependencies = [ "prometheus-parse", "proptest", "rand 0.8.5", - "reqwest", + "reqwest 0.12.28", "rolling-file", "rosetta-core", "rusqlite", @@ -10418,7 +10466,7 @@ dependencies = [ "num-bigint 0.4.6", "pocket-ic", "prometheus-parse", - "reqwest", + "reqwest 0.12.28", "rosetta-core", "serde", "serde_json", @@ -10434,7 +10482,7 @@ dependencies = [ "candid", "icrc-ledger-types", "pocket-ic", - "reqwest", + "reqwest 0.12.28", "tempfile", "tokio", ] @@ -10866,7 +10914,7 @@ dependencies = [ "indicatif", "on_wire", "proptest", - "reqwest", + "reqwest 0.12.28", "rosetta-core", "rusqlite", "serde", @@ -11135,7 +11183,7 @@ dependencies = [ "futures", "hex", "maplit", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", "sha2 0.10.9", @@ -11339,7 +11387,7 @@ dependencies = [ "prometheus-parse", "rand 0.8.5", "registry-canister", - "reqwest", + "reqwest 0.12.28", "serde", "serde_bytes", "serde_cbor", @@ -12770,7 +12818,7 @@ dependencies = [ "pretty_assertions", "prost 0.13.5", "rand 0.8.5", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", "slog", @@ -12948,7 +12996,7 @@ dependencies = [ "ic-test-utilities-types", "ic-types", "prost 0.13.5", - "reqwest", + "reqwest 0.12.28", "serde", "serde_cbor", "serde_json", @@ -13738,7 +13786,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "registry-canister", - "reqwest", + "reqwest 0.12.28", "rolling-file", "rosetta-core", "rusqlite", @@ -13770,7 +13818,7 @@ dependencies = [ "icp-ledger", "nix 0.24.3", "rand 0.8.5", - "reqwest", + "reqwest 0.12.28", "rosetta-core", "serde", "serde_json", @@ -14430,7 +14478,7 @@ dependencies = [ "pocket-ic", "rand 0.8.5", "rand_chacha 0.3.1", - "reqwest", + "reqwest 0.12.28", "rust_decimal", "serde", "slog", @@ -14898,7 +14946,7 @@ dependencies = [ "rand_chacha 0.3.1", "regex", "registry-canister", - "reqwest", + "reqwest 0.12.28", "ring", "serde", "serde_bytes", @@ -15252,7 +15300,7 @@ dependencies = [ "once_cell", "rand 0.8.5", "registry-canister", - "reqwest", + "reqwest 0.12.28", "serde", "slog", "ssh2", @@ -15341,7 +15389,7 @@ dependencies = [ "ic_consensus_system_test_utils", "ic_consensus_threshold_sig_system_test_utils", "icrc-ledger-types", - "reqwest", + "reqwest 0.12.28", "serde_json", "slog", ] @@ -15803,7 +15851,7 @@ dependencies = [ "prometheus", "proptest", "rand 0.8.5", - "reqwest", + "reqwest 0.12.28", "slog", "tempfile", "test-strategy 0.4.5", @@ -15993,7 +16041,7 @@ dependencies = [ "prost 0.13.5", "rand 0.8.5", "registry-canister", - "reqwest", + "reqwest 0.12.28", "rsa", "serde_json", "slog", @@ -16043,7 +16091,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "registry-canister", - "reqwest", + "reqwest 0.12.28", "serde_cbor", "serde_json", "slog", @@ -16120,7 +16168,7 @@ dependencies = [ "leb128", "p256", "rand 0.8.5", - "reqwest", + "reqwest 0.12.28", "rsa", "serde", "serde_bytes", @@ -16517,7 +16565,7 @@ dependencies = [ "ic-types", "ic-universal-canister", "itertools 0.12.1", - "reqwest", + "reqwest 0.12.28", "serde_json", "slog", "ssh2", @@ -17725,6 +17773,9 @@ name = "lru" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] [[package]] name = "lru" @@ -18036,6 +18087,16 @@ dependencies = [ "proptest", ] +[[package]] +name = "meval" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79496a5651c8d57cd033c5add8ca7ee4e3d5f7587a4777484640d9cb60392d9" +dependencies = [ + "fnv", + "nom 1.2.4", +] + [[package]] name = "mime" version = "0.3.17" @@ -18100,9 +18161,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "log", @@ -18243,6 +18304,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "nanoid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" +dependencies = [ + "rand 0.8.5", +] + [[package]] name = "neli" version = "0.6.5" @@ -18294,7 +18364,7 @@ dependencies = [ "ic_consensus_system_test_utils", "prost 0.13.5", "regex", - "reqwest", + "reqwest 0.12.28", "serde_cbor", "slog", "tempfile", @@ -18364,7 +18434,7 @@ dependencies = [ "rand_chacha 0.3.1", "registry-canister", "rejoin-test-lib", - "reqwest", + "reqwest 0.12.28", "serde", "serde_cbor", "serde_json", @@ -18530,7 +18600,7 @@ dependencies = [ "on_wire", "prost 0.13.5", "registry-canister", - "reqwest", + "reqwest 0.12.28", "serde", "serde_cbor", "slog", @@ -18595,6 +18665,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca" +[[package]] +name = "nom" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" + [[package]] name = "nom" version = "7.1.3" @@ -19282,6 +19358,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-multimap" version = "0.7.3" @@ -19325,7 +19410,7 @@ dependencies = [ "ic-utils 0.45.0", "ic_consensus_system_test_utils", "itertools 0.12.1", - "reqwest", + "reqwest 0.12.28", "serde", "slog", "tempfile", @@ -19907,7 +19992,7 @@ dependencies = [ "maplit", "prost 0.13.5", "registry-canister", - "reqwest", + "reqwest 0.12.28", "schemars 0.8.22", "semver", "serde", @@ -20017,7 +20102,7 @@ dependencies = [ "prometheus", "rcgen 0.13.2", "registry-canister", - "reqwest", + "reqwest 0.12.28", "rustls 0.23.37", "serde", "serde_cbor", @@ -21592,11 +21677,50 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.4.2", "web-sys", "webpki-roots 1.0.6", ] +[[package]] +name = "reqwest" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.13", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower 0.5.3", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams 0.5.0", + "web-sys", +] + [[package]] name = "resolv-conf" version = "0.7.6" @@ -21679,6 +21803,38 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "rig-core" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac7d75266ac1f79b1faeff611c85cb40be00a0a983db10036591424f0433dc1" +dependencies = [ + "as-any", + "async-stream", + "base64 0.22.1", + "bytes", + "eventsource-stream", + "fastrand", + "futures", + "futures-timer", + "glob", + "http 1.4.0", + "mime", + "mime_guess", + "nanoid", + "ordered-float 5.3.0", + "pin-project-lite", + "reqwest 0.13.3", + "schemars 1.2.1", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", + "tracing-futures", + "url", +] + [[package]] name = "ring" version = "0.17.14" @@ -21894,7 +22050,7 @@ dependencies = [ "icp-ledger", "on_wire", "rand 0.8.5", - "reqwest", + "reqwest 0.12.28", "rosetta-core", "serde", "serde_cbor", @@ -22387,7 +22543,7 @@ checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "dyn-clone", "indexmap 2.13.0", - "schemars_derive", + "schemars_derive 0.8.22", "serde", "serde_json", ] @@ -22412,6 +22568,7 @@ checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", + "schemars_derive 1.2.1", "serde", "serde_json", ] @@ -22428,6 +22585,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "schemars_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.117", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -22484,7 +22653,7 @@ dependencies = [ "ic-registry-subnet-type", "ic-system-test-driver", "ic_consensus_system_test_utils", - "reqwest", + "reqwest 0.12.28", "serde_json", "slog", ] @@ -23012,7 +23181,7 @@ dependencies = [ "mockall", "pem", "rcgen 0.13.2", - "reqwest", + "reqwest 0.12.28", "sev", "tempfile", "tokio", @@ -23846,7 +24015,7 @@ dependencies = [ "ic-nervous-system-common-test-keys", "ic-nns-common", "ic-nns-constants", - "reqwest", + "reqwest 0.12.28", "rgb", "serde", "serde_json", @@ -24397,9 +24566,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.50.0" +version = "1.52.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" dependencies = [ "bytes", "libc", @@ -24425,9 +24594,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -24876,6 +25045,18 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "futures", + "futures-task", + "pin-project", + "tracing", +] + [[package]] name = "tracing-journald" version = "0.3.2" @@ -25378,7 +25559,7 @@ dependencies = [ "lalrpop 0.22.2", "lz4_flex", "nom-language", - "ordered-float", + "ordered-float 4.6.0", "regex", "serde", "serde_json", @@ -25429,7 +25610,7 @@ dependencies = [ "ic-http-utils", "mockall", "procfs", - "reqwest", + "reqwest 0.12.28", "rusb", "serde", "serde_json", @@ -25772,6 +25953,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasm_fuzzers" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index ab83cc148cba..0d1b2c74b3ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ members = [ "packages/icrc-ledger-types", "packages/pocket-ic", "packages/pocket-ic/test_canister", + "rs/ai_agent", "rs/artifact_pool", "rs/backup", "rs/bitcoin/adapter", diff --git a/bazel/rust.MODULE.bazel b/bazel/rust.MODULE.bazel index cd10f87f534a..aebc3a04d116 100644 --- a/bazel/rust.MODULE.bazel +++ b/bazel/rust.MODULE.bazel @@ -939,6 +939,10 @@ crate.spec( package = "memchr", version = "2.7", ) +crate.spec( + package = "meval", + version = "^0.2.0", +) crate.spec( # When updating this, please make sure that the built # binary exports metrics http_cache_* after one @@ -1221,6 +1225,21 @@ crate.spec( package = "quinn", version = "^0.11.5", ) +crate.spec( + # Pinned at =0.11.7 deliberately. Newer point releases (0.11.13+) added + # an unconditional `ring = { features = ["wasm32_unknown_unknown_js"] }` + # and `getrandom = { features = ["wasm_js"] }` for `wasm32-unknown-unknown`. + # Bazel's crate_universe target-unifies features per package, so those + # transitively activate `getrandom 0.2.10/js` (= wasm-bindgen + js-sys) + # for *every* wasm32 crate in the workspace -- which causes IC canister + # wasms to fail Wasm-validation with `Module imports function + # '__wbindgen_describe' from '__wbindgen_placeholder__' that is not + # exported by the runtime`. Holding quinn-proto at 0.11.7 keeps the + # NNS canister Wasm clean while letting the host-side reqwest 0.12 + # (which transitively pulls quinn) keep working. + package = "quinn-proto", + version = "=0.11.7", +) crate.spec( package = "quinn-udp", version = "^0.5.5", @@ -1304,6 +1323,18 @@ crate.spec( package = "ring", version = "^0.17.7", ) +crate.spec( + # See ai_agent docs / commit history for the full saga. Short version: + # we keep rig at default-features = false, features = ["reqwest"], and + # supply the missing TLS plumbing for reqwest 0.13 via the + # `rustls-no-provider` feature -- not by enabling rig's `rustls` + # feature (that drags in aws-lc-rs and conflicts with our pinned + # quinn 0.11.5). + default_features = False, + features = ["reqwest"], + package = "rig-core", + version = "^0.36.0", +) crate.spec( package = "ripemd", version = "^0.1.1", @@ -2061,6 +2092,39 @@ crate.annotation( crate = "libz-sys", crate_features = ["static"], ) +crate.annotation( + # rig 0.36 transitively builds reqwest 0.13, but rig only enables + # reqwest's `charset, http2, system-proxy` features -- never any TLS + # backend. As a result, reqwest 0.13's `Client::builder().build()` + # falls back to the plain-HTTP hyper connector and every https:// + # request fails synchronously with `client error (Connect) -> + # invalid URL, scheme is not http`. + # + # Activate `rustls-no-provider` (which gates the rustls connector + # code in reqwest source) *and* also inject the rustls deps the + # feature would normally pull in -- `crate.annotation crate_features` + # only sets compile-time `cfg(feature = ...)` flags; it does NOT + # re-trigger Cargo's dep resolution, so we have to wire the deps in + # by hand via the `deps` attribute. We deliberately use + # `rustls-no-provider` (not `rustls`) so that aws-lc-rs is not + # pulled in alongside the workspace's existing `ring` rustls + # provider; the ai_agent main() installs the ring provider at + # startup so reqwest's rustls path picks it up. + crate = "reqwest", + crate_features = [ + "__rustls", + "__tls", + "rustls-no-provider", + ], + version = "0.13.3", + deps = [ + "@crate_index__hyper-rustls-0.27.7//:hyper_rustls", + "@crate_index__rustls-0.23.27//:rustls", + "@crate_index__rustls-pki-types-1.12.0//:rustls_pki_types", + "@crate_index__rustls-platform-verifier-0.6.2//:rustls_platform_verifier", + "@crate_index__tokio-rustls-0.26.0//:tokio_rustls", + ], +) crate.annotation( crate = "curve25519-dalek", rustc_flags = [ diff --git a/ic-os/components/guestos.bzl b/ic-os/components/guestos.bzl index 14b11e727f1b..25350338fb7a 100644 --- a/ic-os/components/guestos.bzl +++ b/ic-os/components/guestos.bzl @@ -69,6 +69,16 @@ def component_files(mode): Label("guestos/ollama/generate-ollama-tls-cert.service"): "/etc/systemd/system/generate-ollama-tls-cert.service", Label("guestos/ollama/ollama-tls.conf"): "/etc/stunnel/ollama-tls.conf", Label("guestos/ollama/ollama-tls.service"): "/etc/systemd/system/ollama-tls.service", + # IC AI agent orchestration HTTP API (started on AI nodes only, + # alongside ollama). Same TLS-via-stunnel pattern: the agent + # listens on 127.0.0.1:11501 and stunnel terminates TLS on + # :::11500. All three units are explicitly disabled in the + # GuestOS Dockerfile so non-AI nodes never run them. + Label("guestos/ai-agent/ic-ai-agent.service"): "/etc/systemd/system/ic-ai-agent.service", + Label("guestos/ai-agent/ic-ai-agent-tls.service"): "/etc/systemd/system/ic-ai-agent-tls.service", + Label("guestos/ai-agent/ic-ai-agent-tls.conf"): "/etc/stunnel/ic-ai-agent-tls.conf", + Label("guestos/ai-agent/generate-ic-ai-agent-tls-cert.sh"): "/opt/ic/bin/generate-ic-ai-agent-tls-cert.sh", + Label("guestos/ai-agent/generate-ic-ai-agent-tls-cert.service"): "/etc/systemd/system/generate-ic-ai-agent-tls-cert.service", Label("guestos/remote-attestation-server.service"): "/etc/systemd/system/remote-attestation-server.service", Label("guestos/generate-ic-config/generate-ic-config.service"): "/etc/systemd/system/generate-ic-config.service", Label("guestos/share/ic-boundary.env"): "/opt/ic/share/ic-boundary.env", diff --git a/ic-os/components/guestos/ai-agent/generate-ic-ai-agent-tls-cert.service b/ic-os/components/guestos/ai-agent/generate-ic-ai-agent-tls-cert.service new file mode 100644 index 000000000000..ff7c77a2284c --- /dev/null +++ b/ic-os/components/guestos/ai-agent/generate-ic-ai-agent-tls-cert.service @@ -0,0 +1,18 @@ +[Unit] +Description=Generate self-signed TLS cert for the ic-ai-agent stunnel proxy +Documentation=man:openssl-req(1) + +After=var.mount local-fs.target +RequiresMountsFor=/var/lib + +ConditionPathExists=!/var/lib/ic-ai-agent-tls/stunnel.pem + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/opt/ic/bin/generate-ic-ai-agent-tls-cert.sh + +[Install] +# Disabled by default in the GuestOS Dockerfile. Started on demand by +# manage-ai-agent.sh, itself driven by the orchestrator's AiNodeManager. +WantedBy=multi-user.target diff --git a/ic-os/components/guestos/ai-agent/generate-ic-ai-agent-tls-cert.sh b/ic-os/components/guestos/ai-agent/generate-ic-ai-agent-tls-cert.sh new file mode 100755 index 000000000000..61618713c0fb --- /dev/null +++ b/ic-os/components/guestos/ai-agent/generate-ic-ai-agent-tls-cert.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# Generate a self-signed TLS certificate used by stunnel to terminate TLS in +# front of the local ic-ai-agent backend. +# +# Mirrors generate-ollama-tls-cert.sh in shape; idempotent oneshot. + +set -eu + +CERT_DIR=/var/lib/ic-ai-agent-tls +CERT_FILE="${CERT_DIR}/cert.pem" +KEY_FILE="${CERT_DIR}/key.pem" +COMBINED_FILE="${CERT_DIR}/stunnel.pem" + +mkdir -p "${CERT_DIR}" +chmod 0750 "${CERT_DIR}" + +if [ -s "${CERT_FILE}" ] && [ -s "${KEY_FILE}" ] && [ -s "${COMBINED_FILE}" ]; then + echo "TLS material already present at ${CERT_DIR}, nothing to do." >&2 + exit 0 +fi + +# Stable Subject CN derived from the machine-id. +CN="ic-ai-agent" +if [ -s /etc/machine-id ]; then + CN="ic-ai-agent-$(cat /etc/machine-id)" +fi + +umask 077 + +if ! openssl req \ + -x509 \ + -newkey rsa:2048 \ + -keyout "${KEY_FILE}" \ + -out "${CERT_FILE}" \ + -days 3650 \ + -nodes \ + -subj "/CN=${CN}" \ + -addext "subjectAltName=DNS:${CN},DNS:localhost,IP:127.0.0.1,IP:0.0.0.0" \ + 2>/tmp/openssl-stderr.$$; then + echo "openssl req failed:" >&2 + cat /tmp/openssl-stderr.$$ >&2 || true + rm -f /tmp/openssl-stderr.$$ + exit 1 +fi +rm -f /tmp/openssl-stderr.$$ + +cat "${CERT_FILE}" "${KEY_FILE}" >"${COMBINED_FILE}" + +TARGET_GROUP="root" +if getent group stunnel4 >/dev/null 2>&1; then + TARGET_GROUP="stunnel4" +fi +chown "root:${TARGET_GROUP}" "${KEY_FILE}" "${CERT_FILE}" "${COMBINED_FILE}" +chmod 0640 "${KEY_FILE}" "${CERT_FILE}" "${COMBINED_FILE}" + +echo "Generated self-signed TLS cert at ${CERT_FILE} for CN=${CN} (group=${TARGET_GROUP})." >&2 diff --git a/ic-os/components/guestos/ai-agent/ic-ai-agent-tls.conf b/ic-os/components/guestos/ai-agent/ic-ai-agent-tls.conf new file mode 100644 index 000000000000..fa136d9f7ea4 --- /dev/null +++ b/ic-os/components/guestos/ai-agent/ic-ai-agent-tls.conf @@ -0,0 +1,24 @@ +# stunnel configuration for the IC AI agent TLS reverse proxy. +# +# Terminates TLS on :::11500 with a self-signed certificate generated at +# first boot by generate-ic-ai-agent-tls-cert.service, and forwards +# plaintext traffic to the local agent backend on 127.0.0.1:11501. + +# Run in foreground so systemd supervises the main PID directly. +foreground = yes +pid = + +# Drop privileges after binding to the listener. +setuid = stunnel4 +setgid = stunnel4 + +# Combined PEM (cert + key). +cert = /var/lib/ic-ai-agent-tls/stunnel.pem + +# We don't authenticate clients (untrusted self-signed cert anyway). +verify = 0 + +[ic-ai-agent] +# IPv6 wildcard with dual-stack enabled accepts both v4 and v6. +accept = :::11500 +connect = 127.0.0.1:11501 diff --git a/ic-os/components/guestos/ai-agent/ic-ai-agent-tls.service b/ic-os/components/guestos/ai-agent/ic-ai-agent-tls.service new file mode 100644 index 000000000000..025b39067348 --- /dev/null +++ b/ic-os/components/guestos/ai-agent/ic-ai-agent-tls.service @@ -0,0 +1,27 @@ +[Unit] +Description=stunnel TLS terminator in front of the IC AI agent +Documentation=man:stunnel(8) + +# Mirrors the design of ollama-tls.service: stunnel runs independently of +# ic-ai-agent.service and just listens on 11500/tcp, forwarding plaintext +# to 127.0.0.1:11501. If the agent isn't running, clients get a refused +# upstream connection but the TLS listener stays up. +# +# Do NOT use BindsTo=ic-ai-agent.service; the agent service is explicitly +# disabled in the GuestOS Dockerfile and BindsTo would cause systemd to +# Stop this unit at boot. +After=network-online.target generate-ic-ai-agent-tls-cert.service +Wants=network-online.target generate-ic-ai-agent-tls-cert.service + +# The cert is mandatory; if the generator failed, refuse to start. +ConditionPathExists=/var/lib/ic-ai-agent-tls/stunnel.pem + +[Service] +Type=simple +ExecStart=/usr/bin/stunnel /etc/stunnel/ic-ai-agent-tls.conf + +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target diff --git a/ic-os/components/guestos/ai-agent/ic-ai-agent.service b/ic-os/components/guestos/ai-agent/ic-ai-agent.service new file mode 100644 index 000000000000..69caed6ab670 --- /dev/null +++ b/ic-os/components/guestos/ai-agent/ic-ai-agent.service @@ -0,0 +1,49 @@ +[Unit] +Description=IC AI agent orchestration HTTP API (disabled by default) +Documentation=https://github.com/anomalyco/opencode + +# Disabled by default in the GuestOS Dockerfile. Started on demand by +# manage-ai-agent.sh (driven by the orchestrator's AiNodeManager) together +# with the TLS cert generator and the stunnel proxy. Regular non-AI nodes +# never bring this up. + +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple + +# Run as the dedicated `ic-ai-agent` system user. That user is set up in +# the GuestOS Dockerfile and joined to: +# +# * `nonconfidential` — read access to /var/lib/ic/data/ic_state +# * `ic-registry-local-store` — read access to the local registry store +# +# We deliberately do NOT use DynamicUser=yes here: the IC observability +# tools (`ic_state`, `ic_metrics`, `ic_logs`) need to read directories +# owned by `ic-replica:nonconfidential`, which a dynamic UID (with no +# static group memberships) cannot do. +User=ic-ai-agent +Group=ic-ai-agent + +# Bind to loopback only on a non-privileged port. External clients reach +# the service through the stunnel TLS terminator on :::11500 (see +# /etc/systemd/system/ic-ai-agent-tls.service), which forwards plaintext to +# 127.0.0.1:11501. +Environment=IC_AI_AGENT_ADDR=127.0.0.1:11501 + +ExecStart=/opt/ic/bin/ic-ai-agent --addr 127.0.0.1:11501 + +Restart=on-failure +RestartSec=5s + +# The agent is a stateless HTTP server: no on-disk state of its own. +# Light hardening that doesn't conflict with reading the replica's +# state/registry trees. +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +NoNewPrivileges=yes + +[Install] +WantedBy=multi-user.target diff --git a/ic-os/components/guestos/ollama/manage-ollama.sh b/ic-os/components/guestos/ollama/manage-ollama.sh index ff3288c029ac..11e67b184aa7 100755 --- a/ic-os/components/guestos/ollama/manage-ollama.sh +++ b/ic-os/components/guestos/ollama/manage-ollama.sh @@ -18,16 +18,28 @@ ACTION="$1" # of this up. case "$ACTION" in start) - # Cert must exist before stunnel starts; service is `Type=oneshot` - # with `RemainAfterExit=yes`, so `start` is idempotent. + # Cert must exist before stunnel starts; cert services are + # `Type=oneshot` with `RemainAfterExit=yes`, so `start` is + # idempotent. /bin/systemctl start generate-ollama-tls-cert.service /bin/systemctl start ollama-tls.service /bin/systemctl start ollama.service + + # The IC AI agent service runs alongside ollama on AI nodes, + # exposing an HTTP orchestration API on a separate TLS port + # (11500). It's started with the same lifecycle as ollama: any + # node that flips to AI mode brings both up, any node that flips + # away brings both down. + /bin/systemctl start generate-ic-ai-agent-tls-cert.service + /bin/systemctl start ic-ai-agent-tls.service + /bin/systemctl start ic-ai-agent.service ;; stop) - # Stop in reverse order. The cert generator is `RemainAfterExit=yes` - # and has nothing to tear down; leave it active so the cert remains + # Stop in reverse order. Cert generators are `RemainAfterExit=yes` + # and have nothing to tear down; leave them active so certs remain # valid for the next start. + /bin/systemctl stop ic-ai-agent.service + /bin/systemctl stop ic-ai-agent-tls.service /bin/systemctl stop ollama.service /bin/systemctl stop ollama-tls.service ;; diff --git a/ic-os/guestos/context/Dockerfile b/ic-os/guestos/context/Dockerfile index f871ddccbc73..bf5a7c1f523f 100644 --- a/ic-os/guestos/context/Dockerfile +++ b/ic-os/guestos/context/Dockerfile @@ -116,7 +116,10 @@ RUN systemctl disable \ fstrim.timer \ ollama.service \ ollama-tls.service \ - generate-ollama-tls-cert.service + generate-ollama-tls-cert.service \ + ic-ai-agent.service \ + ic-ai-agent-tls.service \ + generate-ic-ai-agent-tls-cert.service # ------ GUESTOS WORK -------------------------------------------- @@ -254,6 +257,26 @@ RUN addgroup --system ollama && \ chown ollama:ollama /var/lib/ollama && \ chmod 0750 /var/lib/ollama +# The "ic-ai-agent" account. Used to run the `ic-ai-agent` HTTP service +# (started on AI nodes by manage-ai-agent.sh, driven by the +# orchestrator's AiNodeManager). +# +# The agent's IC-observability tools need read access to two on-disk +# trees written by the state-sync replica: +# +# * /var/lib/ic/data/ic_state (group nonconfidential) +# * /var/lib/ic/data/ic_registry_local_store +# (group ic-registry-local-store) +# +# Both are set up by setup-permissions.sh as group-readable. We add +# `ic-ai-agent` to those groups here so the unix permission check passes; +# without this the service runs under DynamicUser/an isolated UID and +# gets EACCES walking the checkpoints directory. +RUN addgroup --system ic-ai-agent && \ + adduser --system --disabled-password --shell /usr/sbin/nologin --no-create-home --ingroup ic-ai-agent -c "IC AI Agent" ic-ai-agent && \ + adduser ic-ai-agent nonconfidential && \ + adduser ic-ai-agent ic-registry-local-store + # ------ INSTALL SCRIPTS ----------------------------------------- # Install IC binaries and other data late -- this means everything above diff --git a/ic-os/guestos/defs.bzl b/ic-os/guestos/defs.bzl index d512a313aafe..a1c8b82f5edc 100644 --- a/ic-os/guestos/defs.bzl +++ b/ic-os/guestos/defs.bzl @@ -55,6 +55,7 @@ def image_deps(mode, malicious = False): "//rs/ic_os/release:custom_metrics": "/opt/ic/bin/custom_metrics:0755", # Collects and reports custom metrics. "//rs/ic_os/remote_attestation/server": "/opt/ic/bin/remote_attestation_server:0755", # Remote Attestation service "//rs/ic_os/guest_upgrade/client": "/opt/ic/bin/guest_upgrade_client:0755", # Disk encryption key exchange client + "//rs/ai_agent:ic-ai-agent": "/opt/ic/bin/ic-ai-agent:0755", # AI agent orchestration HTTP API (started on AI nodes) # additional libraries to install "//rs/ic_os/release:nss_icos": "/usr/lib/x86_64-linux-gnu/libnss_icos.so.2:0644", # Allows referring to the guest IPv6 by name guestos from host, and host as hostos from guest. diff --git a/publish/binaries/BUILD.bazel b/publish/binaries/BUILD.bazel index 322d621df5d8..b3a721544dec 100644 --- a/publish/binaries/BUILD.bazel +++ b/publish/binaries/BUILD.bazel @@ -9,6 +9,7 @@ ALL_BINARIES = { # Keep sorted "canister_sandbox": "//rs/canister_sandbox", "compiler_sandbox": "//rs/canister_sandbox:compiler_sandbox", + "ic-ai-agent": "//rs/ai_agent:ic-ai-agent", "ic-btc-adapter": "//rs/bitcoin/adapter:ic-btc-adapter", "replica": "//rs/replica:replica", "rate-limiting-canister-client": "//rs/boundary_node/rate_limits/canister_client:rate-limiting-canister-client", diff --git a/rs/ai_agent/BUILD.bazel b/rs/ai_agent/BUILD.bazel new file mode 100644 index 000000000000..64a8707c31b5 --- /dev/null +++ b/rs/ai_agent/BUILD.bazel @@ -0,0 +1,60 @@ +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +DEPENDENCIES = [ + # Keep sorted. + "//rs/config", + "//rs/interfaces/registry", + "//rs/protobuf", + "//rs/registry/client", + "//rs/registry/helpers", + "//rs/registry/local_store", + "//rs/state_layout", + "//rs/types/base_types", + "//rs/types/types", + "@crate_index//:anyhow", + "@crate_index//:axum", + "@crate_index//:chrono", + "@crate_index//:hex", + "@crate_index//:lru", + "@crate_index//:meval", + "@crate_index//:prometheus-parse", + "@crate_index//:reqwest", + "@crate_index//:rig_core", + "@crate_index//:serde", + "@crate_index//:serde_json", + "@crate_index//:slog", + "@crate_index//:slog-async", + "@crate_index//:slog-term", + "@crate_index//:tempfile", + "@crate_index//:thiserror", + "@crate_index//:tokio", + "@crate_index//:tower", + "@crate_index//:tower-http", + "@crate_index//:uuid", +] + +rust_library( + name = "ai_agent", + srcs = glob( + ["src/**/*.rs"], + exclude = ["src/main.rs"], + ), + crate_name = "ic_ai_agent", + deps = DEPENDENCIES, +) + +rust_binary( + name = "ic-ai-agent", + srcs = ["src/main.rs"], + visibility = [ + "//ic-os/guestos:__subpackages__", + "//rs:release-pkg", + ], + deps = DEPENDENCIES + [ + ":ai_agent", + "@crate_index//:clap", + "@crate_index//:rustls", + ], +) diff --git a/rs/ai_agent/Cargo.toml b/rs/ai_agent/Cargo.toml new file mode 100644 index 000000000000..6e59d2f1e820 --- /dev/null +++ b/rs/ai_agent/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "ic-ai-agent" +version.workspace = true +authors.workspace = true +edition.workspace = true +description.workspace = true +documentation.workspace = true + +[[bin]] +name = "ic-ai-agent" +path = "src/main.rs" + +[dependencies] +anyhow = { workspace = true } +axum = { workspace = true } +chrono = { workspace = true } +clap = { workspace = true, features = ["env"] } +hex = { workspace = true } +ic-base-types = { path = "../types/base_types" } +ic-config = { path = "../config" } +ic-interfaces-registry = { path = "../interfaces/registry" } +ic-protobuf = { path = "../protobuf" } +ic-registry-client = { path = "../registry/client" } +ic-registry-client-helpers = { path = "../registry/helpers" } +ic-registry-local-store = { path = "../registry/local_store" } +ic-state-layout = { path = "../state_layout" } +ic-types = { path = "../types/types" } +lru = "0.7.8" +meval = "0.2" +prometheus-parse = { workspace = true } +reqwest = { workspace = true } +rig-core = { version = "0.36", default-features = false, features = ["reqwest"] } +rustls = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +slog = { workspace = true } +slog-async = { workspace = true } +slog-term = { workspace = true } +tempfile = { workspace = true } +thiserror = { workspace = true } +tokio = { workspace = true } +tower = { workspace = true } +tower-http = { workspace = true } +uuid = { workspace = true } diff --git a/rs/ai_agent/src/config.rs b/rs/ai_agent/src/config.rs new file mode 100644 index 000000000000..b8b5b6d89a70 --- /dev/null +++ b/rs/ai_agent/src/config.rs @@ -0,0 +1,105 @@ +//! Runtime configuration for the AI agent service. +//! +//! In v1 the only required configuration is the Gemini API key, which is +//! supplied at runtime via `POST /v1/config` rather than via env vars (see +//! the spec). Defaults below cover the rest. + +use serde::{Deserialize, Serialize}; +use std::{path::PathBuf, time::Duration}; + +use crate::sessions::{DEFAULT_IDLE_TTL, DEFAULT_MAX_SESSIONS}; + +/// Default Gemini model used if `/v1/config` doesn't override it. +/// +/// `gemini-2.0-flash` was retired for new users; switching to `flash-latest` +/// keeps the agent working without pinning to a specific (and deprecatable) +/// version. Callers can still override per-config via the `model` field. +pub const DEFAULT_GEMINI_MODEL: &str = "gemini-flash-latest"; + +/// Default agent system prompt. +/// +/// IC observability tools (`ic_state`, `ic_metrics`) are described here so +/// the LLM knows to reach for them. Without an explicit mention, the model +/// tends to fall back to "I don't have access to live data" answers instead +/// of using the tools wired into the agent. +/// +/// `ic_logs` is intentionally not advertised here — it's a TODO (see +/// `tools/ic_logs.rs`). When it lands, add it back to this prompt with a +/// description of when to prefer it over `ic_metrics`. +pub const DEFAULT_PREAMBLE: &str = "You are a concise, helpful assistant. \ + When tools are provided, prefer using them for any factual, computational, \ + or time-sensitive request. \ + \ + You can also query Internet Computer node observability: `ic_state` for \ + canister/subnet/node metadata read from the locally synced state, and \ + `ic_metrics` for replica/orchestrator/host Prometheus metrics. Prefer \ + `ic_state` for \"what exists\" and `ic_metrics` for \"how is it \ + performing\". Always cite the metric name you used."; + +/// Default cap on tool-call turns per agent invocation. +pub const DEFAULT_MAX_TURNS: usize = 5; + +/// Default replica config file path on a deployed GuestOS / AiNode. The +/// orchestrator places `ic.json5` here and we re-parse it (with a tmpdir for +/// any path-resolution helpers it does internally) to discover the on-disk +/// state root that the AiNode's state-sync replica writes to **and** the +/// registry local store path used to look up peer node IPv6 addresses. +pub const DEFAULT_IC_CONFIG_PATH: &str = "/run/ic-node/config/ic.json5"; + +/// Static configuration baked in at startup. The actual provider client is +/// created later, when `/v1/config` is invoked with the API key. +#[derive(Clone, Debug)] +pub struct AppConfig { + pub default_model: String, + pub default_preamble: String, + pub default_max_turns: usize, + /// Path to the replica `ic.json5` config. Used by `ic_state` to discover + /// the on-disk state root, and by `ic_metrics` to discover the + /// registry local store (so node ids can be resolved to IPv6 + /// addresses of peer nodes in the syncing subnet). + pub ic_config_path: PathBuf, + /// Maximum number of concurrently-cached chat sessions. Tuned for + /// AI-node operator workloads, not for serving end users at scale. + pub max_sessions: usize, + /// Per-session idle TTL. A session that hasn't received a turn in + /// this long is dropped on next access. + pub session_idle_ttl: Duration, +} + +impl Default for AppConfig { + fn default() -> Self { + Self { + default_model: DEFAULT_GEMINI_MODEL.to_string(), + default_preamble: DEFAULT_PREAMBLE.to_string(), + default_max_turns: DEFAULT_MAX_TURNS, + ic_config_path: PathBuf::from(DEFAULT_IC_CONFIG_PATH), + max_sessions: DEFAULT_MAX_SESSIONS, + session_idle_ttl: DEFAULT_IDLE_TTL, + } + } +} + +/// Body of `POST /v1/config`. Currently only Gemini is supported. +#[derive(Debug, Deserialize)] +pub struct ConfigRequest { + /// Provider name. Defaults to `gemini`. + #[serde(default = "default_provider")] + pub provider: String, + /// Provider API key. Required for `gemini`. + pub api_key: String, + /// Optional model override. + pub model: Option, + /// Optional default preamble override. + pub preamble: Option, +} + +fn default_provider() -> String { + "gemini".to_string() +} + +#[derive(Debug, Serialize)] +pub struct ConfigResponse { + pub status: &'static str, + pub provider: String, + pub model: String, +} diff --git a/rs/ai_agent/src/handlers/chat.rs b/rs/ai_agent/src/handlers/chat.rs new file mode 100644 index 000000000000..eeba41639959 --- /dev/null +++ b/rs/ai_agent/src/handlers/chat.rs @@ -0,0 +1,180 @@ +use std::sync::Arc; + +use axum::{Json, extract::State, http::StatusCode, response::IntoResponse}; +use rig::completion::Prompt; +use slog::{info, warn}; + +use crate::{ + models::{ChatRequest, ChatResponse, ErrorBody}, + providers::{AiProvider, provider_not_configured}, + state::AppState, + tools::validate_tool_names, +}; + +/// `POST /v1/agent/chat` — multi-turn agent invocation with +/// server-managed transcript. +/// +/// Wire shape: +/// * Without `session_id`: the server creates a fresh session with +/// an empty transcript, runs the prompt, and returns the freshly- +/// minted session id alongside the response. +/// * With `session_id`: the server replays the cached transcript +/// into the agent, runs the new prompt, and appends the resulting +/// turn (user message + any tool-call interleavings + assistant +/// reply) back into the cached transcript. +/// +/// The agent itself is rebuilt per request — that's cheap (struct +/// construction + tool wiring), and it picks up `POST /v1/config` +/// changes naturally. Per-request `preamble`, `tools`, and +/// `max_turns` always apply. +pub async fn chat( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + if req.prompt.trim().is_empty() { + return ( + StatusCode::BAD_REQUEST, + Json(ErrorBody::new("prompt must not be empty")), + ) + .into_response(); + } + if let Err(unknown) = validate_tool_names(&req.tools) { + return ( + StatusCode::BAD_REQUEST, + Json(ErrorBody::new(format!("unknown tool: {unknown}"))), + ) + .into_response(); + } + + // Resolve the provider once for this request. + let provider = match read_provider(&state) { + Ok(p) => p, + Err(resp) => return *resp, + }; + + // Pull the cached transcript (if any). The snapshot is owned — + // we never hold the store mutex across the LLM `.await`. + let (mut history, session_id, is_new) = match req.session_id.as_deref() { + Some(id) => match state.sessions.get(id) { + Some(snap) => (snap.history, id.to_string(), false), + None => { + // Caller passed an id we don't know (or that has + // expired). Honour the id by keying the new session + // under it, rather than minting a different one and + // leaving the caller's id orphaned. + (Vec::new(), id.to_string(), true) + } + }, + None => (Vec::new(), crate::sessions::SessionStore::fresh_id(), true), + }; + + let preamble = req + .preamble + .as_deref() + .unwrap_or(state.config.default_preamble.as_str()); + let max_turns = req.max_turns.unwrap_or(state.config.default_max_turns); + + let agent = match provider.build_agent(&state, preamble, &[]).await { + Ok(a) => a, + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ErrorBody::new(format!("agent build failed: {e}"))), + ) + .into_response(); + } + }; + + // We use `prompt(...).with_history(...).extended_details()` rather + // than `Chat::chat(...)` because the former returns a + // `PromptResponse` whose `messages` field carries the new turns + // (user prompt + tool calls/results + final assistant) in the + // right order. That's exactly what we need to append to the + // cached transcript so the next turn sees a faithful history, + // including any tool-call interleavings. + let result = agent + .prompt(req.prompt.as_str()) + .with_history(history.clone()) + .max_turns(max_turns) + .extended_details() + .await; + + match result { + Ok(resp) => { + // Extend the snapshot with the new turns produced by this + // request. `messages` is `Option>`; rig only + // sets `None` when no new turn happened (defensive — in + // practice it's always populated for prompt requests). + if let Some(new_turns) = resp.messages.as_ref() { + history.extend(new_turns.iter().cloned()); + } + + // Persist the updated transcript. For new sessions this + // is the first `put`; for existing ones it's an + // `update_history` (which silently no-ops if the session + // was evicted while we were `await`ing — see the comment + // on `SessionStore::update_history`). + if is_new { + state.sessions.put( + Some(session_id.clone()), + history, + provider.name(), + provider.model().to_string(), + ); + info!( + state.log, + "created chat session"; + "session_id" => &session_id, + "provider" => provider.name(), + "model" => provider.model() + ); + } else { + state.sessions.update_history(&session_id, history); + } + + let body = ChatResponse { + response: resp.output, + session_id, + provider: provider.name().to_string(), + model: provider.model().to_string(), + }; + (StatusCode::OK, Json(body)).into_response() + } + Err(e) => { + warn!(state.log, "agent chat failed"; "error" => %e, "session_id" => &session_id); + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ErrorBody::new(format!("Agent failed: {e}"))), + ) + .into_response() + } + } +} + +/// Read the active provider, returning a 503 response if `/v1/config` +/// hasn't been called yet (or the lock is poisoned, which would mean +/// a previous `/v1/config` write panicked mid-update — best handled +/// the same way as "not configured"). +/// +/// The error variant is boxed so the resulting `Result` stays small; +/// `axum::response::Response` is ~128 bytes and `clippy::result_large_err` +/// otherwise complains. +fn read_provider(state: &Arc) -> Result> { + let guard = match state.provider.read() { + Ok(g) => g, + Err(_) => return Err(Box::new(provider_unavailable())), + }; + match guard.as_ref() { + Some(p) => Ok(p.clone()), + None => Err(Box::new(provider_unavailable())), + } +} + +/// Build the 503 "provider not configured" response. +fn provider_unavailable() -> axum::response::Response { + ( + StatusCode::SERVICE_UNAVAILABLE, + Json(ErrorBody::new(provider_not_configured().to_string())), + ) + .into_response() +} diff --git a/rs/ai_agent/src/handlers/config.rs b/rs/ai_agent/src/handlers/config.rs new file mode 100644 index 000000000000..ffd960784e62 --- /dev/null +++ b/rs/ai_agent/src/handlers/config.rs @@ -0,0 +1,59 @@ +use std::sync::Arc; + +use axum::{Json, extract::State, http::StatusCode, response::IntoResponse}; +use slog::{info, warn}; + +use crate::{ + config::{ConfigRequest, ConfigResponse}, + models::ErrorBody, + providers::AiProvider, + state::AppState, +}; + +/// `POST /v1/config` — install/replace the active provider client. +/// +/// Until this endpoint is called successfully, `/v1/agent/run` and +/// `/v1/agent/chat` return 503. +pub async fn configure( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match AiProvider::from_request(&req, &state.config.default_model) { + Ok(provider) => { + let resp = ConfigResponse { + status: "ok", + provider: provider.name().to_string(), + model: provider.model().to_string(), + }; + // Sync RwLock; we don't `.await` while the guard is alive. + // A poisoned lock means a prior writer panicked — recover + // by overwriting the inner value, which is what the + // `into_inner()` recovery pattern would do anyway. + match state.provider.write() { + Ok(mut guard) => *guard = Some(provider), + Err(poisoned) => *poisoned.into_inner() = Some(provider), + } + // Drop all cached chat sessions: their cached transcripts + // were produced under the previous model/credential. Mixing + // them across a reconfiguration is more confusing than + // helpful, especially for key rotations. + let cleared = state.sessions.clear(); + info!( + state.log, + "provider configured"; + "provider" => &resp.provider, + "model" => &resp.model, + "sessions_cleared" => cleared + ); + (StatusCode::OK, Json(resp)).into_response() + } + Err(e) => { + warn!(state.log, "provider config rejected"; "error" => %e); + ( + StatusCode::BAD_REQUEST, + Json(ErrorBody::new(format!("invalid config: {e}"))), + ) + .into_response() + } + } +} diff --git a/rs/ai_agent/src/handlers/health.rs b/rs/ai_agent/src/handlers/health.rs new file mode 100644 index 000000000000..2bf3891f6410 --- /dev/null +++ b/rs/ai_agent/src/handlers/health.rs @@ -0,0 +1,24 @@ +use std::sync::Arc; + +use axum::{Json, extract::State}; + +use crate::{models::HealthResponse, state::AppState}; + +/// `GET /v1/health` — liveness probe; reports active provider/model if any. +pub async fn health(State(state): State>) -> Json { + // A poisoned provider lock can only happen if a panic occurred + // while it was held write-locked, which would mean the process + // is in a degraded state already; in that case it's still + // sensible to report "ok" with no provider so liveness probes + // can detect the degraded state via the missing fields. + let provider = state.provider.read().ok(); + let (provider_name, model) = match provider.as_deref().and_then(|opt| opt.as_ref()) { + Some(p) => (Some(p.name().to_string()), Some(p.model().to_string())), + None => (None, None), + }; + Json(HealthResponse { + status: "ok", + provider: provider_name, + model, + }) +} diff --git a/rs/ai_agent/src/handlers/mod.rs b/rs/ai_agent/src/handlers/mod.rs new file mode 100644 index 000000000000..8096e55d19e3 --- /dev/null +++ b/rs/ai_agent/src/handlers/mod.rs @@ -0,0 +1,5 @@ +pub mod chat; +pub mod config; +pub mod health; +pub mod run; +pub mod sessions; diff --git a/rs/ai_agent/src/handlers/run.rs b/rs/ai_agent/src/handlers/run.rs new file mode 100644 index 000000000000..7487c705b12e --- /dev/null +++ b/rs/ai_agent/src/handlers/run.rs @@ -0,0 +1,117 @@ +use std::sync::Arc; + +use axum::{Json, extract::State, http::StatusCode, response::IntoResponse}; +use rig::completion::Prompt; +use slog::warn; + +use crate::{ + models::{ErrorBody, RunRequest, RunResponse}, + providers::provider_not_configured, + state::AppState, + tools::validate_tool_names, +}; + +/// Render the full `Error::source()` chain of `e` as " -> "-separated text. +fn error_chain(e: &dyn std::error::Error) -> String { + let mut parts = Vec::new(); + let mut cur: Option<&dyn std::error::Error> = e.source(); + while let Some(s) = cur { + parts.push(s.to_string()); + cur = s.source(); + } + parts.join(" -> ") +} + +/// `POST /v1/agent/run` — single-turn agent invocation. +pub async fn run( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + if req.prompt.trim().is_empty() { + return ( + StatusCode::BAD_REQUEST, + Json(ErrorBody::new("prompt must not be empty")), + ) + .into_response(); + } + if let Err(unknown) = validate_tool_names(&req.tools) { + return ( + StatusCode::BAD_REQUEST, + Json(ErrorBody::new(format!("unknown tool: {unknown}"))), + ) + .into_response(); + } + + // `state.provider` is a sync RwLock; we never hold it across an + // `.await`. Clone the active provider out and drop the guard + // immediately by going out of scope at the end of this block. + let provider = { + let provider_guard = match state.provider.read() { + Ok(g) => g, + Err(_) => { + return ( + StatusCode::SERVICE_UNAVAILABLE, + Json(ErrorBody::new(provider_not_configured().to_string())), + ) + .into_response(); + } + }; + match provider_guard.as_ref() { + Some(p) => p.clone(), + None => { + return ( + StatusCode::SERVICE_UNAVAILABLE, + Json(ErrorBody::new(provider_not_configured().to_string())), + ) + .into_response(); + } + } + }; + + let preamble = req + .preamble + .as_deref() + .unwrap_or(state.config.default_preamble.as_str()); + let max_turns = req.max_turns.unwrap_or(state.config.default_max_turns); + + let agent = match provider.build_agent(&state, preamble, &req.context).await { + Ok(a) => a, + Err(e) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ErrorBody::new(format!("agent build failed: {e}"))), + ) + .into_response(); + } + }; + + let result = agent + .prompt(req.prompt.as_str()) + .max_turns(max_turns) + .extended_details() + .await; + + match result { + Ok(resp) => { + let turns_used = resp.messages.as_ref().map(Vec::len).unwrap_or(1); + let body = RunResponse { + response: resp.output, + turns_used, + provider: provider.name().to_string(), + model: provider.model().to_string(), + }; + (StatusCode::OK, Json(body)).into_response() + } + Err(e) => { + // rig wraps the underlying reqwest/rustls error in + // `CompletionError::HttpError`, whose Display impl drops the + // source chain. Walk `Error::source()` ourselves so the + // failing transport-level cause makes it into the log / + // response body where it can actually be debugged. + let chain = error_chain(&e); + warn!(state.log, "agent run failed"; "error" => %e, "chain" => &chain); + let msg = format!("Agent failed: {e}: {chain}"); + (StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorBody::new(msg))).into_response() + } + } +} diff --git a/rs/ai_agent/src/handlers/sessions.rs b/rs/ai_agent/src/handlers/sessions.rs new file mode 100644 index 000000000000..81394a8db2c7 --- /dev/null +++ b/rs/ai_agent/src/handlers/sessions.rs @@ -0,0 +1,61 @@ +//! `DELETE /v1/agent/sessions[/:id]` — drop cached chat sessions. +//! +//! Two routes: +//! * `DELETE /v1/agent/sessions/:id` — drop one session. +//! * `DELETE /v1/agent/sessions` — drop all of them. +//! +//! Both return `200` with `{ "status": "ok", "cleared": }`. The +//! single-id path returns `404` when the id is unknown. + +use std::sync::Arc; + +use axum::{ + Json, + extract::{Path, State}, + http::StatusCode, + response::IntoResponse, +}; +use slog::info; + +use crate::{ + models::{ClearResponse, ErrorBody}, + state::AppState, +}; + +/// Drop one session by id. +pub async fn delete_one( + State(state): State>, + Path(id): Path, +) -> impl IntoResponse { + if state.sessions.remove(&id) { + info!(state.log, "session deleted"; "session_id" => &id); + ( + StatusCode::OK, + Json(ClearResponse { + status: "ok", + cleared: 1, + }), + ) + .into_response() + } else { + ( + StatusCode::NOT_FOUND, + Json(ErrorBody::new(format!("unknown session_id: {id}"))), + ) + .into_response() + } +} + +/// Drop every cached session. +pub async fn delete_all(State(state): State>) -> impl IntoResponse { + let cleared = state.sessions.clear(); + info!(state.log, "all sessions cleared"; "count" => cleared); + ( + StatusCode::OK, + Json(ClearResponse { + status: "ok", + cleared, + }), + ) + .into_response() +} diff --git a/rs/ai_agent/src/lib.rs b/rs/ai_agent/src/lib.rs new file mode 100644 index 000000000000..0f65b8c740dc --- /dev/null +++ b/rs/ai_agent/src/lib.rs @@ -0,0 +1,16 @@ +//! IC AI agent orchestration library. +//! +//! Lightweight HTTP API exposing single-turn (`/v1/agent/run`) and +//! multi-turn (`/v1/agent/chat`) agent endpoints, backed by the +//! [`rig`](https://docs.rig.rs/) library. Gemini is the default and currently +//! only supported provider; new providers are added by extending +//! [`providers::AiProvider`]. + +pub mod config; +pub mod handlers; +pub mod models; +pub mod providers; +pub mod router; +pub mod sessions; +pub mod state; +pub mod tools; diff --git a/rs/ai_agent/src/main.rs b/rs/ai_agent/src/main.rs new file mode 100644 index 000000000000..7409837d0124 --- /dev/null +++ b/rs/ai_agent/src/main.rs @@ -0,0 +1,81 @@ +//! IC AI agent service binary entry point. +//! +//! Starts an Axum server exposing the agent orchestration HTTP API +//! described in `ai-agents-in-nodes-spec.md`. The server is plaintext; +//! a stunnel sidecar terminates TLS for external traffic. + +use clap::Parser; +use ic_ai_agent::{ + config::{AppConfig, DEFAULT_IC_CONFIG_PATH}, + router::build_router, + sessions::{DEFAULT_IDLE_TTL, DEFAULT_MAX_SESSIONS}, + state::AppState, +}; +use slog::{Drain, Logger, info, o}; +use std::{net::SocketAddr, path::PathBuf, sync::Arc, time::Duration}; + +#[derive(Debug, Parser)] +#[command(name = "ic-ai-agent", about = "IC AI agent orchestration HTTP API")] +struct Cli { + /// Address to bind the HTTP server to. + #[arg(long, env = "IC_AI_AGENT_ADDR", default_value = "127.0.0.1:11501")] + addr: SocketAddr, + + /// Path to the replica `ic.json5` config. Used by `ic_state` to find + /// the on-disk state root, and by `ic_metrics` to find the registry + /// local store for resolving peer node IPv6 addresses. + #[arg(long, env = "IC_AI_AGENT_IC_CONFIG", default_value = DEFAULT_IC_CONFIG_PATH)] + ic_config: PathBuf, + + /// Maximum number of concurrently-cached chat sessions before the + /// LRU starts evicting. + #[arg(long, env = "IC_AI_AGENT_MAX_SESSIONS", default_value_t = DEFAULT_MAX_SESSIONS)] + max_sessions: usize, + + /// Per-session idle TTL in seconds. Sessions untouched for longer + /// than this are evicted on next access. + #[arg(long, env = "IC_AI_AGENT_SESSION_IDLE_TTL_SECS", default_value_t = DEFAULT_IDLE_TTL.as_secs())] + session_idle_ttl_secs: u64, +} + +fn make_logger() -> Logger { + let decorator = slog_term::TermDecorator::new().build(); + let drain = slog_term::FullFormat::new(decorator).build().fuse(); + let drain = slog_async::Async::new(drain).build().fuse(); + Logger::root(drain, o!("component" => "ic-ai-agent")) +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let cli = Cli::parse(); + let log = make_logger(); + + // Install the process-wide rustls crypto provider exactly once. + // + // rig 0.36 transitively pulls reqwest 0.13, which we configure with + // `rustls-no-provider` to avoid linking aws-lc-rs alongside the + // workspace's existing ring provider (mixing the two crashes rustls + // 0.23 at startup with `no process-level CryptoProvider available`). + // The flip side of `rustls-no-provider` is that *no* provider is + // auto-installed -- so we have to do it ourselves before the first + // `reqwest::Client::builder().build()` call inside rig. Without + // this, every Gemini request fails synchronously with the opaque + // `error sending request for url (...)` you see wrapped by rig as + // `CompletionError::HttpError`. + let _ = rustls::crypto::ring::default_provider().install_default(); + + let config = AppConfig { + ic_config_path: cli.ic_config.clone(), + max_sessions: cli.max_sessions, + session_idle_ttl: Duration::from_secs(cli.session_idle_ttl_secs), + ..AppConfig::default() + }; + let state = Arc::new(AppState::new(config, log.clone())); + let app = build_router(state); + + info!(log, "ic-ai-agent listening"; "addr" => %cli.addr); + + let listener = tokio::net::TcpListener::bind(cli.addr).await?; + axum::serve(listener, app.into_make_service()).await?; + Ok(()) +} diff --git a/rs/ai_agent/src/models/mod.rs b/rs/ai_agent/src/models/mod.rs new file mode 100644 index 000000000000..23e8bf76624e --- /dev/null +++ b/rs/ai_agent/src/models/mod.rs @@ -0,0 +1,7 @@ +//! Wire types for HTTP request/response bodies. + +pub mod request; +pub mod response; + +pub use request::{ChatRequest, RunRequest}; +pub use response::{ChatResponse, ClearResponse, ErrorBody, HealthResponse, RunResponse}; diff --git a/rs/ai_agent/src/models/request.rs b/rs/ai_agent/src/models/request.rs new file mode 100644 index 000000000000..db720dc84716 --- /dev/null +++ b/rs/ai_agent/src/models/request.rs @@ -0,0 +1,36 @@ +use serde::Deserialize; + +/// Body of `POST /v1/agent/run`. +#[derive(Debug, Deserialize)] +pub struct RunRequest { + pub prompt: String, + pub preamble: Option, + #[serde(default)] + pub context: Vec, + #[serde(default)] + pub tools: Vec, + pub max_turns: Option, +} + +/// Body of `POST /v1/agent/chat`. +/// +/// The session_id is what makes this multi-turn: omit it on the first +/// call to start a new session (the server returns the freshly-minted +/// id), then echo the same id on every subsequent turn. The server +/// caches the conversation transcript per session id; only the new +/// user prompt needs to be sent each time. +/// +/// `preamble`, `tools`, and `max_turns` apply per-request — the agent +/// is rebuilt each turn from the cached transcript, so changing them +/// between turns simply changes how the next turn is run. +#[derive(Debug, Deserialize)] +pub struct ChatRequest { + pub prompt: String, + /// Existing session id. If omitted, a new session is created and + /// the id is returned in the response. + pub session_id: Option, + pub preamble: Option, + #[serde(default)] + pub tools: Vec, + pub max_turns: Option, +} diff --git a/rs/ai_agent/src/models/response.rs b/rs/ai_agent/src/models/response.rs new file mode 100644 index 000000000000..e1705dacc85a --- /dev/null +++ b/rs/ai_agent/src/models/response.rs @@ -0,0 +1,51 @@ +use serde::Serialize; + +#[derive(Debug, Serialize)] +pub struct HealthResponse { + pub status: &'static str, + pub provider: Option, + pub model: Option, +} + +#[derive(Debug, Serialize)] +pub struct RunResponse { + pub response: String, + pub turns_used: usize, + pub provider: String, + pub model: String, +} + +/// Body of a successful `POST /v1/agent/chat` response. +/// +/// `session_id` is always present — either echoed back or freshly +/// minted on first turn. Clients should persist it and pass it on +/// every subsequent turn. +#[derive(Debug, Serialize)] +pub struct ChatResponse { + pub response: String, + pub session_id: String, + pub provider: String, + pub model: String, +} + +/// Body of `DELETE /v1/agent/sessions` and +/// `DELETE /v1/agent/sessions/:id`. +#[derive(Debug, Serialize)] +pub struct ClearResponse { + /// "ok" on success. + pub status: &'static str, + /// How many sessions were dropped (0 or 1 for single-session + /// delete; 0..=N for the bulk delete). + pub cleared: usize, +} + +#[derive(Debug, Serialize)] +pub struct ErrorBody { + pub error: String, +} + +impl ErrorBody { + pub fn new(msg: impl Into) -> Self { + Self { error: msg.into() } + } +} diff --git a/rs/ai_agent/src/providers/mod.rs b/rs/ai_agent/src/providers/mod.rs new file mode 100644 index 000000000000..348e87d19218 --- /dev/null +++ b/rs/ai_agent/src/providers/mod.rs @@ -0,0 +1,113 @@ +//! Provider abstraction. +//! +//! Wraps the underlying `rig` provider client behind an enum so new +//! providers can be added without touching handler code: extend +//! [`AiProvider`] with a new variant, add an arm in [`AiProvider::from_request`] +//! and in [`AiProvider::build_agent`], done. + +use std::sync::Arc; + +use anyhow::anyhow; +use rig::{ + agent::Agent, + client::CompletionClient, + providers::gemini::{Client as GeminiClient, completion::CompletionModel as GeminiCompletion}, +}; +use slog::warn; + +use crate::{ + config::ConfigRequest, + state::AppState, + tools::{Calculator, CurrentDateTime, IcMetrics, IcState}, +}; + +/// Active AI provider client. Currently Gemini-only; new providers slot in +/// here as additional variants. +#[derive(Clone)] +pub enum AiProvider { + Gemini { client: GeminiClient, model: String }, +} + +impl AiProvider { + /// Build a provider client from a `POST /v1/config` body. + pub fn from_request(req: &ConfigRequest, default_model: &str) -> anyhow::Result { + match req.provider.as_str() { + "gemini" => { + if req.api_key.trim().is_empty() { + return Err(anyhow!("api_key must not be empty")); + } + let client = GeminiClient::new(&req.api_key) + .map_err(|e| anyhow!("failed to construct Gemini client: {e}"))?; + let model = req + .model + .clone() + .unwrap_or_else(|| default_model.to_string()); + Ok(Self::Gemini { client, model }) + } + other => Err(anyhow!("unknown provider: {other}")), + } + } + + pub fn name(&self) -> &'static str { + match self { + AiProvider::Gemini { .. } => "gemini", + } + } + + pub fn model(&self) -> &str { + match self { + AiProvider::Gemini { model, .. } => model.as_str(), + } + } + + /// Build a configured agent. Tools and context can be filtered/added by + /// the caller, but in v1 we always wire all built-in tools so requests + /// can reference any of them by name. + /// + /// Takes `Arc` so the IC observability tools (`ic_state`, + /// `ic_metrics`) can pick up the shared replica config path and + /// lazily-built node directory. Tools that fail to construct + /// (typically `ic_state` on a node where `ic.json5` is missing or + /// unreadable) are skipped with a warning rather than failing the + /// whole agent build — the other tools may still be useful and we + /// don't want one bad path to take the agent down. + /// + /// `ic_logs` is intentionally not wired up here — it's a TODO, + /// see `tools/ic_logs.rs`. + pub async fn build_agent( + &self, + state: &Arc, + preamble: &str, + contexts: &[String], + ) -> anyhow::Result> { + match self { + AiProvider::Gemini { client, model } => { + let mut builder = client.agent(model).preamble(preamble); + for ctx in contexts { + builder = builder.context(ctx); + } + let mut builder = builder.tool(Calculator).tool(CurrentDateTime); + + match IcState::new(state.clone()).await { + Ok(t) => builder = builder.tool(t), + Err(e) => { + warn!( + state.log, + "skipping ic_state tool: {}", e; + "ic_config_path" => %state.config.ic_config_path.display() + ); + } + } + + builder = builder.tool(IcMetrics::new(state.clone())); + + Ok(builder.build()) + } + } + } +} + +/// Shared error message helper for missing-provider conditions. +pub fn provider_not_configured() -> anyhow::Error { + anyhow!("provider not configured; POST /v1/config first") +} diff --git a/rs/ai_agent/src/router.rs b/rs/ai_agent/src/router.rs new file mode 100644 index 000000000000..d91b474245dc --- /dev/null +++ b/rs/ai_agent/src/router.rs @@ -0,0 +1,34 @@ +//! Axum router wiring. + +use std::sync::Arc; + +use axum::{ + Router, + routing::{delete, get, post}, +}; +use tower_http::trace::TraceLayer; + +use crate::{ + handlers::{ + chat::chat, + config::configure, + health::health, + run::run, + sessions::{delete_all as delete_all_sessions, delete_one as delete_one_session}, + }, + state::AppState, +}; + +pub fn build_router(state: Arc) -> Router { + Router::new() + .route("/v1/health", get(health)) + .route("/v1/config", post(configure)) + .route("/v1/agent/run", post(run)) + .route("/v1/agent/chat", post(chat)) + // Drop a single chat session (404 if unknown). + .route("/v1/agent/sessions/{id}", delete(delete_one_session)) + // Wipe every cached session at once. + .route("/v1/agent/sessions", delete(delete_all_sessions)) + .layer(TraceLayer::new_for_http()) + .with_state(state) +} diff --git a/rs/ai_agent/src/sessions.rs b/rs/ai_agent/src/sessions.rs new file mode 100644 index 000000000000..bcbbe0c5c908 --- /dev/null +++ b/rs/ai_agent/src/sessions.rs @@ -0,0 +1,192 @@ +//! In-memory chat session store. +//! +//! `/v1/agent/chat` is multi-turn: callers send only the new user +//! prompt plus a `session_id`, and the server is responsible for +//! correlating turns. We cache the conversation transcript +//! (`Vec`) per session id; the rig `Agent` itself is rebuilt +//! per turn (it's cheap — just struct construction and tool wiring) +//! so that re-keying via `POST /v1/config` and per-request preamble +//! changes naturally take effect. +//! +//! Bounding rules (so a misbehaving client can't OOM the process): +//! +//! * `LruCache` capped at a configurable count +//! (default [`DEFAULT_MAX_SESSIONS`]). +//! * Per-session idle TTL ([`DEFAULT_IDLE_TTL`] default). Idle entries +//! are swept lazily on every access — no background task required. +//! +//! Reset semantics: +//! +//! * `DELETE /v1/agent/sessions/:id` and `DELETE /v1/agent/sessions` +//! call into [`SessionStore::remove`] / [`SessionStore::clear`]. +//! * `POST /v1/config` clears all sessions, because per-session +//! transcripts under a different model/key tend to confuse the +//! model on continuation. + +use std::{ + sync::Mutex, + time::{Duration, Instant}, +}; + +use lru::LruCache; +use rig::completion::message::Message; +use uuid::Uuid; + +/// Default cap on concurrently-cached sessions. ~256 chat threads +/// covers any realistic AI-node operator workload; raise via +/// `--max-sessions` if you actually need more. +pub const DEFAULT_MAX_SESSIONS: usize = 256; + +/// Default idle TTL. A session that hasn't received a turn in this +/// long is dropped on the next access. +pub const DEFAULT_IDLE_TTL: Duration = Duration::from_secs(60 * 60); + +/// One cached chat thread. +pub struct Session { + /// Conversation transcript. Replayed verbatim into the next + /// `agent.prompt(...).with_history(history)` call. After each + /// turn we extend this with the `messages` list that rig returns + /// in its `PromptResponse` — that list contains the new user + /// prompt, any tool-call/tool-result interleavings, and the final + /// assistant reply, in the right order for the next turn. + pub history: Vec, + + /// Provider name + model snapshot, copied for the response so + /// callers can verify which model handled their turn. + pub provider_name: &'static str, + pub model: String, + + /// Last-touched timestamp for idle-eviction. + pub last_activity: Instant, +} + +/// Thread-safe LRU + TTL session store. Wraps a `Mutex` — +/// the critical section is a hashmap lookup + clone of the history +/// `Vec` and is never held across an `.await`. +pub struct SessionStore { + inner: Mutex>, + idle_ttl: Duration, +} + +impl SessionStore { + pub fn new(max_sessions: usize, idle_ttl: Duration) -> Self { + let cap = max_sessions.max(1); + Self { + inner: Mutex::new(LruCache::new(cap)), + idle_ttl, + } + } + + /// Generate a fresh, server-side session id. UUIDv4 keeps + /// collisions a non-issue and matches what most chat APIs use. + pub fn fresh_id() -> String { + Uuid::new_v4().to_string() + } + + /// Returns a snapshot of the cached session for `id`, refreshing + /// its `last_activity`. Returns `None` if the session does not + /// exist or has expired (in which case the entry is also evicted). + /// + /// The returned [`SessionSnapshot`] holds an owned clone of the + /// transcript; the caller can `await` on the LLM without keeping + /// the store mutex. + pub fn get(&self, id: &str) -> Option { + let mut cache = self.inner.lock().unwrap(); + + // Lazy idle-TTL sweep on the requested entry. We don't sweep + // the whole cache — capacity is bounded so stragglers will be + // pushed out by the LRU on their own. + if let Some(s) = cache.peek(id) + && s.last_activity.elapsed() > self.idle_ttl + { + cache.pop(id); + return None; + } + + let session = cache.get_mut(id)?; + session.last_activity = Instant::now(); + Some(SessionSnapshot { + history: session.history.clone(), + provider_name: session.provider_name, + model: session.model.clone(), + }) + } + + /// Insert (or replace) a session. Returns the id we keyed it + /// under, which may be a freshly-minted UUID if the caller + /// passed `None`. + pub fn put( + &self, + id: Option, + history: Vec, + provider_name: &'static str, + model: String, + ) -> String { + let id = id.unwrap_or_else(Self::fresh_id); + let mut cache = self.inner.lock().unwrap(); + cache.put( + id.clone(), + Session { + history, + provider_name, + model, + last_activity: Instant::now(), + }, + ); + id + } + + /// Replace the cached transcript for `id` after a turn completes. + /// + /// If the session was evicted between the snapshot read and this + /// write (eviction races: another concurrent request, or LRU + /// pressure from a different session id) we silently no-op rather + /// than re-creating the entry — the user's intent was to update + /// an existing session, not to resurrect one that was just told + /// to go away. The next turn will then start fresh under the + /// same id (or a new one) without surprising history. + pub fn update_history(&self, id: &str, history: Vec) { + let mut cache = self.inner.lock().unwrap(); + if let Some(session) = cache.get_mut(id) { + session.history = history; + session.last_activity = Instant::now(); + } + } + + /// Remove one session by id. Returns `true` if it was present. + pub fn remove(&self, id: &str) -> bool { + self.inner.lock().unwrap().pop(id).is_some() + } + + /// Drop every session. Used by `DELETE /v1/agent/sessions` and on + /// every `POST /v1/config` (because mixing transcripts across + /// model/credential changes is more confusing than helpful). + pub fn clear(&self) -> usize { + let mut cache = self.inner.lock().unwrap(); + let n = cache.len(); + cache.clear(); + n + } + + /// Number of cached sessions. Intended for diagnostics / tests. + #[allow(dead_code)] + pub fn len(&self) -> usize { + self.inner.lock().unwrap().len() + } + + /// Whether the cache currently has no sessions. Paired with + /// [`SessionStore::len`] to satisfy `clippy::len_without_is_empty`. + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.inner.lock().unwrap().is_empty() + } +} + +/// Read snapshot of a session. The transcript is owned (cloned out +/// of the LRU) so handlers can drop the store mutex before issuing +/// the LLM call. +pub struct SessionSnapshot { + pub history: Vec, + pub provider_name: &'static str, + pub model: String, +} diff --git a/rs/ai_agent/src/state.rs b/rs/ai_agent/src/state.rs new file mode 100644 index 000000000000..5622082bd7e3 --- /dev/null +++ b/rs/ai_agent/src/state.rs @@ -0,0 +1,87 @@ +//! Per-process shared state. +//! +//! Holds the active provider client (set by `POST /v1/config`) plus static +//! defaults. Wrapped in `Arc>` so `/v1/config` can update it +//! at runtime without recreating the router. + +use crate::{ + config::AppConfig, + providers::AiProvider, + sessions::SessionStore, + tools::node_directory::{NodeDirectory, NodeDirectoryError}, +}; +use slog::Logger; +use std::sync::{Arc, RwLock}; +use tokio::sync::OnceCell; + +/// Mutable runtime state shared across handlers. +pub struct AppState { + pub config: AppConfig, + pub log: Logger, + /// `None` until `POST /v1/config` populates it. + /// + /// `std::sync::RwLock` (not the tokio variant): every call site + /// only holds the guard long enough to clone an `AiProvider` (a + /// cheap struct of `String` + `GeminiClient`) or to write a new + /// one in. None of them `.await` while the guard is alive, which + /// is the criterion for using a blocking lock under tokio. + pub provider: RwLock>, + /// Lazily constructed registry-backed node lookup. Built on first + /// use because (a) it touches the filesystem and we want + /// `AppState::new` to stay infallible, and (b) on a fresh AiNode + /// the registry local store may not exist yet on startup. + node_directory: OnceCell, NodeDirectoryError>>, + /// In-memory chat-session cache for `/v1/agent/chat`. See + /// [`crate::sessions`] for bounding rules. + pub sessions: SessionStore, +} + +impl AppState { + pub fn new(config: AppConfig, log: Logger) -> Self { + let sessions = SessionStore::new(config.max_sessions, config.session_idle_ttl); + Self { + config, + log, + provider: RwLock::new(None), + node_directory: OnceCell::new(), + sessions, + } + } + + /// Returns the shared node directory, constructing it on first + /// call. Errors are cached: if the registry local store is + /// misconfigured we don't keep retrying on every tool call. Restart + /// the service to retry. + pub async fn node_directory(&self) -> Result, NodeDirectoryError> { + let cfg_path = self.config.ic_config_path.clone(); + let res = self + .node_directory + .get_or_init(|| async move { + tokio::task::spawn_blocking(move || NodeDirectory::from_ic_config(&cfg_path)) + .await + .map_err(|e| NodeDirectoryError::Config { + path: std::path::PathBuf::new(), + message: format!("join error: {e}"), + })? + .map(Arc::new) + }) + .await; + match res { + Ok(d) => Ok(Arc::clone(d)), + Err(e) => Err(clone_node_directory_error(e)), + } + } +} + +/// `NodeDirectoryError` doesn't implement `Clone` (the underlying +/// `RegistryClientError` carries a non-cloneable source). For caching +/// purposes we render it through a string round-trip; the lossy +/// display is acceptable here because the cached error is only ever +/// surfaced to the LLM as a one-shot "couldn't reach the registry" +/// message. +fn clone_node_directory_error(e: &NodeDirectoryError) -> NodeDirectoryError { + NodeDirectoryError::Config { + path: std::path::PathBuf::new(), + message: e.to_string(), + } +} diff --git a/rs/ai_agent/src/tools/calculator.rs b/rs/ai_agent/src/tools/calculator.rs new file mode 100644 index 000000000000..6b0087f6cf75 --- /dev/null +++ b/rs/ai_agent/src/tools/calculator.rs @@ -0,0 +1,58 @@ +//! Tool: evaluate a basic arithmetic expression with `meval`. + +use rig::{completion::ToolDefinition, tool::Tool}; +use serde::{Deserialize, Serialize}; +use serde_json::json; + +#[derive(Debug, Deserialize)] +pub struct CalculatorArgs { + pub expression: String, +} + +#[derive(Debug, Serialize)] +pub struct CalculatorOutput { + pub expression: String, + pub result: f64, +} + +#[derive(Debug, thiserror::Error)] +pub enum CalculatorError { + #[error("failed to evaluate expression: {0}")] + Eval(#[from] meval::Error), +} + +pub struct Calculator; + +impl Tool for Calculator { + const NAME: &'static str = "calculator"; + type Error = CalculatorError; + type Args = CalculatorArgs; + type Output = CalculatorOutput; + + async fn definition(&self, _prompt: String) -> ToolDefinition { + ToolDefinition { + name: Self::NAME.to_string(), + description: "Evaluates a basic arithmetic expression and returns the numeric result. \ + Supports +, -, *, /, ^, parentheses, and standard math functions like sin, cos, sqrt." + .to_string(), + parameters: json!({ + "type": "object", + "properties": { + "expression": { + "type": "string", + "description": "An arithmetic expression, e.g. '(144 / 12) * 7'" + } + }, + "required": ["expression"] + }), + } + } + + async fn call(&self, args: Self::Args) -> Result { + let result = meval::eval_str(&args.expression)?; + Ok(CalculatorOutput { + expression: args.expression, + result, + }) + } +} diff --git a/rs/ai_agent/src/tools/current_datetime.rs b/rs/ai_agent/src/tools/current_datetime.rs new file mode 100644 index 000000000000..ac64b7daa93e --- /dev/null +++ b/rs/ai_agent/src/tools/current_datetime.rs @@ -0,0 +1,51 @@ +//! Tool: returns the current UTC date/time. Useful for time-sensitive +//! prompts. + +use chrono::Utc; +use rig::{completion::ToolDefinition, tool::Tool}; +use serde::{Deserialize, Serialize}; +use serde_json::json; + +#[derive(Debug, Deserialize)] +pub struct CurrentDateTimeArgs {} + +#[derive(Debug, Serialize)] +pub struct CurrentDateTimeOutput { + pub utc: String, + pub unix_seconds: i64, +} + +#[derive(Debug, thiserror::Error)] +#[error("current_datetime tool: {0}")] +pub struct CurrentDateTimeError(String); + +pub struct CurrentDateTime; + +impl Tool for CurrentDateTime { + const NAME: &'static str = "current_datetime"; + type Error = CurrentDateTimeError; + type Args = CurrentDateTimeArgs; + type Output = CurrentDateTimeOutput; + + async fn definition(&self, _prompt: String) -> ToolDefinition { + ToolDefinition { + name: Self::NAME.to_string(), + description: "Returns the current UTC date and time, plus the corresponding Unix \ + timestamp in seconds. Takes no arguments." + .to_string(), + parameters: json!({ + "type": "object", + "properties": {}, + "required": [] + }), + } + } + + async fn call(&self, _args: Self::Args) -> Result { + let now = Utc::now(); + Ok(CurrentDateTimeOutput { + utc: now.to_rfc3339(), + unix_seconds: now.timestamp(), + }) + } +} diff --git a/rs/ai_agent/src/tools/ic_logs.rs b/rs/ai_agent/src/tools/ic_logs.rs new file mode 100644 index 000000000000..b7ae73ebcfc2 --- /dev/null +++ b/rs/ai_agent/src/tools/ic_logs.rs @@ -0,0 +1,33 @@ +//! Tool: `ic_logs` — **TODO, not implemented**. +//! +//! Intent: pull recent systemd journal entries for an allow-listed +//! systemd unit (e.g. `ic-replica.service`, `ic-orchestrator.service`) +//! from a peer node, with client-side filtering by time window, +//! priority ceiling, and substring grep. The natural transport is +//! `systemd-journal-gatewayd` on port 19531, which every IC node +//! exposes; the previous draft implementation in this file targeted +//! that endpoint with a `Range: entries=...` header and an allow-list +//! of services. +//! +//! Why it's stubbed for now: the `gatewayd` socket isn't reachable +//! over the IPv6 we resolve from the registry on AI nodes today, so +//! shipping the tool would mean shipping something that always +//! returns transport errors. We'd rather expose the gap explicitly +//! than have the LLM keep retrying a broken endpoint. +//! +//! When picking this back up: +//! * Confirm the journal-gatewayd port and TLS posture across guestos +//! variants (it may need the same `danger_accept_invalid_certs(true)` +//! treatment as `node_exporter`). +//! * Bring back the allow-list of systemd units (replica, orchestrator, +//! crypto-csp, btc/https-outcalls adapters, node_exporter, nftables, +//! chrony) — this is the only thing keeping the LLM from being able +//! to ask for arbitrary host logs. +//! * Re-add the registration in `tools/mod.rs`, `tools/registry.rs`, +//! `providers/mod.rs::build_agent`, and the preamble in `config.rs`. +//! +//! The shared `NodeDirectory` (in `tools/node_directory.rs`) is +//! already wired up for this — `ic_logs` will resolve `node_id -> +//! ipv6` the same way `ic_metrics` does, so reviving the tool is +//! purely a matter of restoring the gatewayd HTTP client + the +//! filtering logic. diff --git a/rs/ai_agent/src/tools/ic_metrics.rs b/rs/ai_agent/src/tools/ic_metrics.rs new file mode 100644 index 000000000000..6bff6d43d320 --- /dev/null +++ b/rs/ai_agent/src/tools/ic_metrics.rs @@ -0,0 +1,755 @@ +//! Tool: `ic_metrics`. +//! +//! Scrapes Prometheus text exposition from one of the IC node's +//! observability endpoints (`replica`, `orchestrator`, `node_exporter`) +//! and answers four kinds of question about the result: +//! +//! * `list` — discover metric names without dumping a 10k-line scrape; +//! * `get` — current samples for a named metric, optionally filtered +//! by labels; +//! * `summary` — a curated dashboard per source so the LLM doesn't +//! have to know which 6 metric names matter for "is the replica +//! healthy?"; +//! * `rate` — per-second delta computed against a tiny in-memory LRU +//! of the previous (timestamp, value) for the same (source, metric, +//! labels) tuple. +//! +//! The target node's IPv6 is resolved through the shared +//! [`NodeDirectory`] from a `node_id` argument. As an escape hatch the +//! caller may pass a raw `ipv6` (e.g. when poking at a not-yet-in- +//! registry node from a dev machine). + +use std::{ + collections::BTreeMap, + net::Ipv6Addr, + str::FromStr, + sync::{Arc, Mutex}, + time::Duration, +}; + +use lru::LruCache; +use prometheus_parse::{Sample, Scrape, Value}; +use rig::{completion::ToolDefinition, tool::Tool}; +use serde::{Deserialize, Serialize}; +use serde_json::json; + +use crate::state::AppState; + +// Per-source URL parameters. Each scrape source on a GuestOS node +// exposes Prometheus exposition with a slightly different shape — +// these constants are the source of truth, verified against live +// nodes: +// +// replica → http://[ipv6]:9090/ (root, plain HTTP) +// orchestrator → http://[ipv6]:9091/ (root, plain HTTP) +// node_exporter → https://[ipv6]:9100/metrics +// (self-signed TLS, /metrics path) +// +// The HTTPS-with-self-signed-cert quirk on node_exporter means we have +// to disable certificate validation on the shared HTTP client; see +// `IcMetrics::new` below. +const REPLICA_PORT: u16 = 9090; +const ORCHESTRATOR_PORT: u16 = 9091; +const NODE_EXPORTER_PORT: u16 = 9100; + +/// HTTP timeout for a single scrape. Generous because `node_exporter` +/// can take a few hundred ms to gather disk stats on a busy box. +const SCRAPE_TIMEOUT: Duration = Duration::from_secs(10); + +/// Cap on the rate-cache size. ~1k entries is roughly 100 metrics +/// across 10 nodes — enough headroom that we never thrash, but bounded +/// so a misbehaving LLM can't grow it without limit. +const RATE_CACHE_CAPACITY: usize = 1024; + +/// Cap on `op = "list"` results. The LLM should never need to see +/// more than this in one shot; if it does, it should narrow the +/// `metric` substring filter. +const LIST_LIMIT: usize = 200; + +#[derive(Debug, thiserror::Error)] +pub enum IcMetricsError { + #[error("invalid arg: {0}")] + InvalidArg(String), + + #[error("http error: {0}")] + Http(#[from] reqwest::Error), + + #[error("upstream {service} returned {status}: {body}")] + Upstream { + service: &'static str, + status: u16, + body: String, + }, + + #[error("prometheus parse error: {0}")] + Prom(String), + + #[error("node directory: {0}")] + Directory(#[from] crate::tools::node_directory::NodeDirectoryError), +} + +#[derive(Debug, Deserialize)] +pub struct MetricsArgs { + /// Source: "replica" | "orchestrator" | "node_exporter". + pub source: String, + + /// Operation: "list" | "get" | "summary" | "rate". + pub op: String, + + /// For "get"/"rate": metric name. For "list": optional substring + /// filter applied to metric names. + pub metric: Option, + + /// For "get"/"rate": optional label filter, e.g. + /// `{"status": "200"}`. All labels must match exactly. + pub labels: Option>, + + /// For "rate": window in seconds. Currently informational — the + /// rate is always computed against whatever previous sample we + /// have for the (source, metric, labels) tuple. Default 60. + pub window_secs: Option, + + /// Textual `NodeId` of the peer node to scrape. Resolved through + /// the registry local store. Either this OR `ipv6` must be + /// provided. + pub node_id: Option, + + /// Optional raw IPv6 override (without brackets). Useful for + /// dev/test before the target is in the registry. + pub ipv6: Option, +} + +#[derive(Debug, Serialize)] +pub struct MetricsOutput { + pub source: String, + pub op: String, + pub target: TargetInfo, + pub data: serde_json::Value, +} + +#[derive(Debug, Serialize)] +pub struct TargetInfo { + pub node_id: Option, + pub ipv6: String, + pub url: String, +} + +/// Source enum plus its scrape port and human label. +#[derive(Clone, Copy, Debug)] +enum Source { + Replica, + Orchestrator, + NodeExporter, +} + +impl Source { + fn parse(s: &str) -> Result { + match s { + "replica" => Ok(Self::Replica), + "orchestrator" => Ok(Self::Orchestrator), + "node_exporter" => Ok(Self::NodeExporter), + other => Err(IcMetricsError::InvalidArg(format!( + "unknown source '{other}'; expected replica|orchestrator|node_exporter" + ))), + } + } + + fn port(self) -> u16 { + match self { + Self::Replica => REPLICA_PORT, + Self::Orchestrator => ORCHESTRATOR_PORT, + Self::NodeExporter => NODE_EXPORTER_PORT, + } + } + + fn label(self) -> &'static str { + match self { + Self::Replica => "replica", + Self::Orchestrator => "orchestrator", + Self::NodeExporter => "node_exporter", + } + } + + /// URL scheme this source listens on. node_exporter terminates TLS + /// (with a self-signed cert); the replica and orchestrator are + /// plain HTTP on a loopback-style admin port. + fn scheme(self) -> &'static str { + match self { + Self::Replica | Self::Orchestrator => "http", + Self::NodeExporter => "https", + } + } + + /// URL path. The replica and orchestrator serve their full + /// exposition at `/`; node_exporter at `/metrics` per upstream + /// convention. + fn path(self) -> &'static str { + match self { + Self::Replica | Self::Orchestrator => "/", + Self::NodeExporter => "/metrics", + } + } +} + +// Curated `summary` metric sets per source. Every name below has been +// verified against a real GuestOS node's exposition output — *not* +// cross-referenced from upstream docs, where naming drifts. Order is +// intentional: most-actionable first, so an LLM that only quotes the +// first few entries still surfaces the most useful signal. +// +// When the IC code base renames a metric, update these lists in lockstep +// — `op = "summary"` will silently return zero samples for a stale name +// rather than erroring. + +/// node_exporter (host VM) — primary triage source. Covers CPU, memory, +/// swap, disk space, disk I/O saturation, network throughput, PSI +/// (pressure stall), clock health, file-descriptor exhaustion, and live +/// TCP socket count. Filesystem and disk metrics are per-mount/per-device, +/// so the LLM should narrow with `op = "get"` + label filter (e.g. +/// `mountpoint="/var/lib/ic/data"` for the IC state partition) to make +/// sense of multi-sample results. +const NODE_EXPORTER_SUMMARY: &[&str] = &[ + "node_load1", + "node_load5", + "node_load15", + "node_memory_MemAvailable_bytes", + "node_memory_MemTotal_bytes", + "node_memory_SwapFree_bytes", + "node_memory_SwapTotal_bytes", + "node_filesystem_avail_bytes", + "node_filesystem_size_bytes", + "node_disk_io_time_seconds_total", + "node_network_receive_bytes_total", + "node_network_transmit_bytes_total", + "node_pressure_io_stalled_seconds_total", + "node_time_seconds", + "node_filefd_allocated", + "node_filefd_maximum", + "node_netstat_Tcp_CurrEstab", +]; + +/// replica (consensus + state-sync internals). Health-focused: certified +/// height, checkpoint count, batch height, block production, peer-count +/// sanity, critical-error counter, RSS. Heavier debugging paths (QUIC, +/// state-sync detail) live behind `op = "get"`. +const REPLICA_SUMMARY: &[&str] = &[ + "state_manager_latest_certified_height", + "state_manager_checkpoints_on_disk_count", + "consensus_batch_height", + "mr_blocks_proposed_total", + "mr_subnet_size", + "critical_errors", + "process_resident_memory_bytes", +]; + +/// orchestrator (replica supervisor + upgrade manager). Small surface; +/// most names are zero in steady state and only move when something is +/// going wrong, which is exactly what we want to surface in a summary. +const ORCHESTRATOR_SUMMARY: &[&str] = &[ + "orchestrator_cup_deserialization_failed_total", + "orchestrator_failed_consecutive_upgrade_checks_total", + "orchestrator_replica_process_start_attempts_total", + "orchestrator_state_removal_failed_total", + "orchestrator_key_rotation_status", + "reboot_duration_seconds", +]; + +/// Cache key for the rate computation. Labels are serialised +/// deterministically (BTreeMap iteration is ordered). +#[derive(Hash, Eq, PartialEq, Clone)] +struct RateKey { + source: &'static str, + target: String, + metric: String, + labels: String, +} + +/// Cached rate-cache entry. We store unix-seconds + the sample value +/// (cast to f64; histogram/counter buckets are read as f64 by +/// `prometheus_parse`). +#[derive(Clone, Copy)] +struct RateEntry { + timestamp_secs: i64, + value: f64, +} + +/// Tool struct. +pub struct IcMetrics { + state: Arc, + /// Shared HTTP client. `reqwest` clients are designed to be + /// long-lived and shared; building one per call would hammer the + /// connection pool and slow scrapes noticeably. + http: reqwest::Client, + /// Last sample for each (source, target, metric, labels) tuple. + /// `std::sync::Mutex` is fine here — the critical section is a + /// hashmap lookup + insert and never blocks on anything async. + rate_cache: Mutex>, +} + +impl IcMetrics { + pub fn new(state: Arc) -> Self { + // node_exporter terminates TLS with a self-signed certificate + // (per GuestOS provisioning). Replica and orchestrator both + // serve plain HTTP, so the only cost of disabling cert + // validation here is on the node_exporter path. We rely on + // (a) reaching the node over an IPv6 address that came from + // the registry and (b) the node_exporter port being firewalled + // to the IC peer mesh — both of which the LLM can't subvert + // through this tool. + let http = reqwest::Client::builder() + .timeout(SCRAPE_TIMEOUT) + .danger_accept_invalid_certs(true) + .build() + .expect("reqwest client builder is infallible with default features"); + Self { + state, + http, + rate_cache: Mutex::new(LruCache::new(RATE_CACHE_CAPACITY)), + } + } + + /// Resolve the target's IPv6 from either `ipv6` (verbatim) or + /// `node_id` (registry lookup). + async fn resolve_target( + &self, + node_id: Option<&str>, + ipv6: Option<&str>, + ) -> Result<(Ipv6Addr, Option), IcMetricsError> { + if let Some(raw) = ipv6 { + let ip = Ipv6Addr::from_str(raw) + .map_err(|e| IcMetricsError::InvalidArg(format!("invalid ipv6 '{raw}': {e}")))?; + return Ok((ip, None)); + } + let nid = node_id.ok_or_else(|| { + IcMetricsError::InvalidArg("either node_id or ipv6 must be provided".to_string()) + })?; + let directory = self.state.node_directory().await?; + let ip = directory.resolve_ipv6(nid)?; + Ok((ip, Some(nid.to_string()))) + } + + /// Build the scrape URL for a given source + IPv6. The shape + /// (scheme, port, path) varies by source — see the comment block + /// next to the port constants at the top of the file for the full + /// matrix. + fn build_url(source: Source, ipv6: Ipv6Addr) -> String { + format!( + "{scheme}://[{ipv6}]:{port}{path}", + scheme = source.scheme(), + port = source.port(), + path = source.path(), + ) + } + + /// Fetch and parse a scrape from the given URL. + async fn fetch_scrape(&self, source: Source, url: &str) -> Result { + let resp = self.http.get(url).send().await?; + let status = resp.status(); + if !status.is_success() { + let body = resp.text().await.unwrap_or_default(); + return Err(IcMetricsError::Upstream { + service: source.label(), + status: status.as_u16(), + body: truncate(&body, 512), + }); + } + let body = resp.text().await?; + // `prometheus_parse::Scrape::parse` wants an iterator of + // `Result` so it can stream over the lines. We + // already have the full body in memory. + let lines = body.lines().map(|l| Ok::<_, std::io::Error>(l.to_string())); + Scrape::parse(lines).map_err(|e| IcMetricsError::Prom(e.to_string())) + } + + fn op_list(scrape: &Scrape, filter: Option<&str>) -> serde_json::Value { + let mut names: Vec<&str> = scrape.docs.keys().map(|s| s.as_str()).collect(); + // Some metrics (e.g. process_*) appear only in `samples`, not + // `docs`. Union the two so the LLM sees everything. + for s in &scrape.samples { + names.push(&s.metric); + } + names.sort(); + names.dedup(); + + let filtered: Vec = match filter { + Some(f) if !f.is_empty() => { + let needle = f; + names + .iter() + .filter(|n| n.contains(needle)) + .map(|n| n.to_string()) + .collect() + } + _ => names.iter().map(|n| n.to_string()).collect(), + }; + + let total = filtered.len(); + let truncated = total > LIST_LIMIT; + let limited: Vec = filtered.into_iter().take(LIST_LIMIT).collect(); + json!({ + "metric_names": limited, + "total": total, + "truncated": truncated, + }) + } + + fn op_get( + scrape: &Scrape, + metric: &str, + labels: Option<&BTreeMap>, + ) -> serde_json::Value { + let samples = filter_samples(scrape, metric, labels); + let rendered: Vec = samples.iter().map(render_sample).collect(); + json!({ + "metric": metric, + "count": rendered.len(), + "samples": rendered, + }) + } + + fn op_summary(scrape: &Scrape, source: Source) -> serde_json::Value { + let names = match source { + Source::Replica => REPLICA_SUMMARY, + Source::Orchestrator => ORCHESTRATOR_SUMMARY, + Source::NodeExporter => NODE_EXPORTER_SUMMARY, + }; + let mut out = serde_json::Map::new(); + for name in names { + let samples = filter_samples(scrape, name, None); + let rendered: Vec = samples.iter().map(render_sample).collect(); + out.insert((*name).to_string(), json!(rendered)); + } + serde_json::Value::Object(out) + } + + fn op_rate( + &self, + scrape: &Scrape, + target: &str, + source: Source, + metric: &str, + labels: Option<&BTreeMap>, + window_secs: u64, + ) -> serde_json::Value { + let samples = filter_samples(scrape, metric, labels); + if samples.is_empty() { + return json!({ + "metric": metric, + "rate": null, + "value": null, + "hint": format!( + "no current sample for {metric} matching the requested labels" + ), + }); + } + + // `rate` is only meaningful on a single time series. If + // multiple samples come back, take the first and surface a + // hint so the LLM tightens its label filter. + let sample = &samples[0]; + let now = chrono::Utc::now().timestamp(); + let value = sample_to_f64(&sample.value); + + let key = RateKey { + source: source.label(), + target: target.to_string(), + metric: metric.to_string(), + labels: serialize_labels(labels), + }; + let prev = self.rate_cache.lock().unwrap().pop(&key); + self.rate_cache.lock().unwrap().put( + key, + RateEntry { + timestamp_secs: now, + value, + }, + ); + + let multi_hint = if samples.len() > 1 { + Some(format!( + "{} samples matched; rate computed from the first. Tighten labels for accuracy.", + samples.len() + )) + } else { + None + }; + + match prev { + Some(prev) => { + let dt = (now - prev.timestamp_secs) as f64; + if dt <= 0.0 { + return json!({ + "metric": metric, + "rate": null, + "value": value, + "hint": "previous sample was not older than the current; ignored", + }); + } + let rate = (value - prev.value) / dt; + let mut out = json!({ + "metric": metric, + "rate": rate, + "rate_per_sec": rate, + "value": value, + "previous_value": prev.value, + "delta_secs": dt, + "window_secs_requested": window_secs, + }); + if let Some(h) = multi_hint { + out["hint"] = serde_json::Value::String(h); + } + out + } + None => { + let mut out = json!({ + "metric": metric, + "rate": null, + "value": value, + "hint": "no prior sample; call rate again later to get a per-second delta", + }); + if let Some(h) = multi_hint { + out["secondary_hint"] = serde_json::Value::String(h); + } + out + } + } + } +} + +/// Filter `scrape.samples` to entries whose metric matches `name` and +/// whose labels are a superset of `required` (if any). +fn filter_samples<'a>( + scrape: &'a Scrape, + name: &str, + required: Option<&BTreeMap>, +) -> Vec<&'a Sample> { + scrape + .samples + .iter() + .filter(|s| s.metric == name) + .filter(|s| match required { + None => true, + Some(req) => req.iter().all(|(k, v)| s.labels.get(k) == Some(v.as_str())), + }) + .collect() +} + +/// Render a `prometheus_parse::Sample` to a small JSON object the LLM +/// can reason about without seeing parser internals. +fn render_sample(s: &&Sample) -> serde_json::Value { + // Sort labels deterministically for stable LLM-facing output — + // `prometheus_parse::Labels` is a HashMap underneath. + let labels: BTreeMap = s + .labels + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + json!({ + "labels": labels, + "value": sample_to_f64(&s.value), + "timestamp_unix_ms": s.timestamp.timestamp_millis(), + "kind": value_kind(&s.value), + }) +} + +/// Cast a `prometheus_parse::Value` to f64 for general comparisons. +/// Histograms and summaries are reduced to their `+Inf`-bucket count +/// (for histograms) or `count` (for summaries), which is the +/// monotonic component callers most often want a rate over. +fn sample_to_f64(v: &Value) -> f64 { + match v { + Value::Counter(x) | Value::Gauge(x) | Value::Untyped(x) => *x, + Value::Histogram(buckets) => { + // `+Inf` bucket = total count. + buckets.iter().last().map(|h| h.count).unwrap_or(0.0) + } + Value::Summary(quantiles) => { + // Summary `count` is not directly exposed by the parser + // here; fall back to the highest-quantile value as a + // monotone-ish proxy. + quantiles.iter().last().map(|q| q.count).unwrap_or(0.0) + } + } +} + +fn value_kind(v: &Value) -> &'static str { + match v { + Value::Counter(_) => "counter", + Value::Gauge(_) => "gauge", + Value::Untyped(_) => "untyped", + Value::Histogram(_) => "histogram", + Value::Summary(_) => "summary", + } +} + +fn serialize_labels(labels: Option<&BTreeMap>) -> String { + match labels { + None => String::new(), + Some(l) => { + let mut parts: Vec = l.iter().map(|(k, v)| format!("{k}={v}")).collect(); + parts.sort(); + parts.join(",") + } + } +} + +fn truncate(s: &str, n: usize) -> String { + if s.len() <= n { + s.to_string() + } else { + format!("{}…", &s[..n]) + } +} + +impl Tool for IcMetrics { + const NAME: &'static str = "ic_metrics"; + type Error = IcMetricsError; + type Args = MetricsArgs; + type Output = MetricsOutput; + + async fn definition(&self, _prompt: String) -> ToolDefinition { + ToolDefinition { + name: Self::NAME.to_string(), + description: "Fetch and analyze Prometheus metrics from one of three IC node \ + sources. Pick `source` based on the question:\n\ + \n\ + * `node_exporter` (guest VM) — DEFAULT for general health, performance, \ + and resource questions: CPU load, memory, swap, disk space, disk I/O \ + saturation, network throughput, file descriptors, TCP socket counts, \ + PSI pressure-stall, clock health. The most useful source for \ + troubleshooting; start here.\n\ + * `replica` — IC-specific consensus and state-sync internals: certified \ + height, checkpoints on disk, block production rate, subnet membership, \ + critical-error counter. Use when the question is about IC liveness or \ + state-sync.\n\ + * `orchestrator` — replica supervisor and upgrade manager: CUP \ + deserialization failures, failed upgrade checks, replica restarts, \ + key rotation status, reboot timing. Use for upgrade and orchestration \ + issues.\n\ + \n\ + Operations: `list` discovers metric names (use a `metric` substring \ + to narrow), `get` returns current samples, `summary` returns a curated \ + dashboard for the chosen source, `rate` computes a per-second delta \ + against the previous call. Targets a specific peer node by `node_id` \ + (resolved via the local registry) or by raw `ipv6`.\n\ + \n\ + IMPORTANT: do NOT query the local node (this AI node). It is a passive \ + state-sync observer, not an active subnet member, so its metrics are \ + not representative of subnet health — replica counters will be near \ + zero, consensus metrics will be missing entirely, and node_exporter \ + will reflect only this AI node's own host. Always pick a `node_id` \ + of an active consensus member (use `ic_state` op=`subnet` to list \ + them and copy a node id from the `nodes` array)." + .to_string(), + parameters: json!({ + "type": "object", + "properties": { + "source": { + "type": "string", + "enum": ["node_exporter", "replica", "orchestrator"], + "description": + "Which scrape endpoint to read. \ + `node_exporter` for host-level CPU/memory/disk/network \ + (preferred default); \ + `replica` for IC consensus/state-sync internals; \ + `orchestrator` for upgrade/CUP/restart issues." + }, + "op": { + "type": "string", + "enum": ["list", "get", "summary", "rate"], + "description": + "What to do with the scrape. list=metric names; \ + get=current samples; summary=curated dashboard; \ + rate=per-second delta vs. previous call." + }, + "metric": { + "type": "string", + "description": + "Metric name. Required for get/rate; optional substring \ + filter for list." + }, + "labels": { + "type": "object", + "additionalProperties": {"type": "string"}, + "description": + "Label exact-match filter, e.g. {\"status\":\"200\"}." + }, + "window_secs": { + "type": "integer", + "minimum": 1, + "description": "Hint for the rate window (default 60)." + }, + "node_id": { + "type": "string", + "description": + "Textual NodeId of the peer to scrape. Resolved through \ + the local registry. Either node_id or ipv6 is required." + }, + "ipv6": { + "type": "string", + "description": + "Raw IPv6 of the peer (without brackets). Override for \ + dev/test." + } + }, + "required": ["source", "op"] + }), + } + } + + async fn call(&self, args: Self::Args) -> Result { + let source = Source::parse(&args.source)?; + let (ipv6, resolved_node_id) = self + .resolve_target(args.node_id.as_deref(), args.ipv6.as_deref()) + .await?; + let url = Self::build_url(source, ipv6); + + // Validate per-op required args before doing the scrape so we + // fail fast on operator mistakes. + match args.op.as_str() { + "get" | "rate" => { + if args.metric.as_deref().unwrap_or("").is_empty() { + return Err(IcMetricsError::InvalidArg(format!( + "metric is required for op={}", + args.op + ))); + } + } + "list" | "summary" => {} + other => { + return Err(IcMetricsError::InvalidArg(format!( + "unknown op '{other}'; expected list|get|summary|rate" + ))); + } + } + + let scrape = self.fetch_scrape(source, &url).await?; + + let data = match args.op.as_str() { + "list" => Self::op_list(&scrape, args.metric.as_deref()), + "get" => Self::op_get(&scrape, args.metric.as_ref().unwrap(), args.labels.as_ref()), + "summary" => Self::op_summary(&scrape, source), + "rate" => self.op_rate( + &scrape, + &ipv6.to_string(), + source, + args.metric.as_ref().unwrap(), + args.labels.as_ref(), + args.window_secs.unwrap_or(60), + ), + // Already validated above. + _ => unreachable!(), + }; + + Ok(MetricsOutput { + source: source.label().to_string(), + op: args.op, + target: TargetInfo { + node_id: resolved_node_id, + ipv6: ipv6.to_string(), + url, + }, + data, + }) + } +} diff --git a/rs/ai_agent/src/tools/ic_state.rs b/rs/ai_agent/src/tools/ic_state.rs new file mode 100644 index 000000000000..e4b05098ab15 --- /dev/null +++ b/rs/ai_agent/src/tools/ic_state.rs @@ -0,0 +1,535 @@ +//! Tool: `ic_state`. +//! +//! Read-only inspector of the replicated state that the AiNode's +//! state-sync replica persists to disk. We don't materialise a full +//! `ReplicatedState` (that would mean wiring up the entire state-manager +//! machinery on every tool call, including page allocators, prometheus +//! registries, and a thread pool, just to answer questions like "who +//! controls canister X?"). Instead we use `ic_state_layout` to point at +//! the on-disk checkpoint directory and decode the individual +//! `*.pbuf` files we care about — `system_metadata.pbuf` for the subnet +//! view, `canister.pbuf` for per-canister state. +//! +//! All paths are derived from `ic.json5` at tool-init time so the tool +//! tracks whatever state directory the orchestrator-managed replica +//! actually writes to. + +use std::{ + path::{Path, PathBuf}, + str::FromStr, + sync::Arc, +}; + +use ic_base_types::{CanisterId, PrincipalId}; +use ic_config::{Config, ConfigSource}; +use ic_protobuf::state::{ + canister_state_bits::v1::CanisterStateBits, system_metadata::v1::SystemMetadata, +}; +use ic_state_layout::{CheckpointLayout, ReadOnly, error::LayoutError}; +use ic_types::Height; +use rig::{completion::ToolDefinition, tool::Tool}; +use serde::{Deserialize, Serialize}; +use serde_json::json; + +use crate::{state::AppState, tools::node_directory::NodeDirectory}; + +/// Subdirectory of `state_root` that holds the verified checkpoints. +/// Mirrors `ic_state_layout::CHECKPOINTS_DIR`. +const CHECKPOINTS_DIR: &str = "checkpoints"; + +#[derive(Debug, thiserror::Error)] +pub enum IcStateError { + #[error("io error reading {path}: {source}")] + Io { + path: PathBuf, + #[source] + source: std::io::Error, + }, + + #[error("state layout error: {0}")] + Layout(#[from] LayoutError), + + #[error("invalid arg: {0}")] + InvalidArg(String), + + #[error("not found: {0}")] + NotFound(String), + + #[error("failed to load replica config from {path}: {message}")] + Config { path: PathBuf, message: String }, +} + +#[derive(Debug, Deserialize)] +pub struct IcStateArgs { + /// Operation to run. One of: + /// - `list_checkpoints` — heights of every verified or state-sync + /// checkpoint currently on disk, ordered ascending. + /// - `subnet` — own-subnet metadata from the latest checkpoint: + /// subnet id, subnet type, node membership, NNS subnet id, batch + /// time, canister id ranges allocated to this subnet. + /// - `list_canisters` — canister ids present in the latest + /// checkpoint, sorted, optionally filtered by substring. + /// - `canister` — full per-canister view (controllers, cycles + /// balance, module hash, freeze threshold, memory/compute + /// allocations, version). + pub op: String, + + /// Specific checkpoint height. Default: the latest checkpoint. + /// Heights are u64; pass as a JSON number. + pub height: Option, + + /// Substring filter for `list_canisters` (matched against the + /// textual canister id, case-insensitive). + pub filter: Option, + + /// Canister id (textual form, e.g. `rrkah-fqaaa-aaaaa-aaaaq-cai`) + /// for `op = "canister"`. + pub canister_id: Option, +} + +#[derive(Debug, Serialize)] +pub struct IcStateOutput { + pub op: String, + pub state_root: String, + pub data: serde_json::Value, +} + +/// Tool struct. Holds the resolved state root path so we don't re-parse +/// `ic.json5` on every call. Re-parsing on every call would also race +/// against the orchestrator if/when it rewrites the file. +pub struct IcState { + state_root: PathBuf, + /// Kept for diagnostics in error messages, for the `state_root` + /// field of every response (so the LLM can confirm which directory + /// it's looking at), and to lazily fetch the shared node + /// directory in `op=subnet`. + state: Arc, +} + +impl IcState { + /// Construct the tool. Parses `ic.json5` to discover the on-disk + /// state root. + /// + /// Async to mirror the spec's `IcState::new(state.clone()).await?` + /// shape (parsing is in fact synchronous; we shell out to + /// `tokio::task::spawn_blocking` to avoid stalling the executor on + /// the file read). + pub async fn new(state: Arc) -> Result { + let cfg_path = state.config.ic_config_path.clone(); + let state_root = tokio::task::spawn_blocking(move || resolve_state_root(&cfg_path)) + .await + .map_err(|e| IcStateError::Config { + path: state.config.ic_config_path.clone(), + message: format!("join error: {e}"), + })??; + Ok(Self { state_root, state }) + } + + /// Convenience constructor used in unit tests where we already know + /// the state root. + #[cfg(test)] + pub fn for_root(state: Arc, state_root: PathBuf) -> Self { + Self { state_root, state } + } + + /// Return the available verified checkpoint heights, ascending. + fn list_checkpoint_heights(&self) -> Result, IcStateError> { + let cps_dir = self.state_root.join(CHECKPOINTS_DIR); + let entries = std::fs::read_dir(&cps_dir).map_err(|e| IcStateError::Io { + path: cps_dir.clone(), + source: e, + })?; + let mut heights = Vec::new(); + for entry in entries { + let entry = entry.map_err(|e| IcStateError::Io { + path: cps_dir.clone(), + source: e, + })?; + if !entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { + continue; + } + // Checkpoint dirs are named with a 16-char zero-padded hex + // height, e.g. "0000000000001234". `state_layout` is the + // source of truth for the format + // (`StateLayout::checkpoint_name`). Skip anything that + // doesn't parse — the directory may also contain markers + // for unverified or in-progress checkpoints in older + // layouts. + let name = entry.file_name(); + let name = name.to_string_lossy(); + if let Ok(h) = u64::from_str_radix(&name, 16) { + heights.push(h); + } + } + heights.sort_unstable(); + Ok(heights) + } + + /// Pick a checkpoint height: caller-supplied if present, else the + /// latest available. + fn resolve_height(&self, requested: Option) -> Result { + let heights = self.list_checkpoint_heights()?; + if heights.is_empty() { + return Err(IcStateError::NotFound(format!( + "no checkpoints under {}", + self.state_root.display() + ))); + } + match requested { + None => Ok(*heights.last().unwrap()), + Some(h) => { + if heights.contains(&h) { + Ok(h) + } else { + Err(IcStateError::NotFound(format!( + "checkpoint at height {h} not found; available: {heights:?}" + ))) + } + } + } + } + + fn checkpoint_layout(&self, height: u64) -> Result, IcStateError> { + let name = format!("{:016x}", height); + let cp_root = self.state_root.join(CHECKPOINTS_DIR).join(name); + Ok(CheckpointLayout::::new_untracked( + cp_root, + Height::new(height), + )?) + } + + async fn op_subnet(&self, height: u64) -> Result { + let cp = self.checkpoint_layout(height)?; + let metadata: SystemMetadata = cp.system_metadata().deserialize()?; + + let own_subnet_id = metadata + .own_subnet_id + .as_ref() + .and_then(principal_to_string); + + let nns_subnet_id = metadata + .network_topology + .as_ref() + .and_then(|nt| nt.nns_subnet_id.as_ref()) + .and_then(principal_to_string); + + // Best-effort: try to resolve node ipv6 addresses through the + // shared registry-backed directory. If the registry isn't + // reachable yet (e.g. fresh AiNode that hasn't synced) we + // simply omit the ipv6 field on each entry — the LLM will + // still see the node ids. + let directory: Option> = self.state.node_directory().await.ok(); + + // Membership of every subnet (including this one) plus subnet + // type. Useful for "how many nodes in subnet Z?" questions. + let mut subnets = Vec::new(); + if let Some(nt) = &metadata.network_topology { + for entry in &nt.subnets { + let sid = entry.subnet_id.as_ref().and_then(principal_to_string); + let topology = entry.subnet_topology.as_ref(); + let node_ids: Vec = topology + .map(|t| { + t.nodes + .iter() + .filter_map(|n| n.node_id.as_ref().and_then(principal_to_string)) + .collect() + }) + .unwrap_or_default(); + let subnet_type = topology.map(|t| t.subnet_type).unwrap_or(0); + + let nodes: Vec = node_ids + .iter() + .map(|nid| { + let ipv6 = directory + .as_ref() + .and_then(|d| d.resolve_ipv6(nid).ok()) + .map(|ip| ip.to_string()); + json!({"node_id": nid, "ipv6": ipv6}) + }) + .collect(); + + subnets.push(json!({ + "subnet_id": sid, + "node_count": nodes.len(), + "nodes": nodes, + "subnet_type": subnet_type, + })); + } + } + + // Canister id ranges allocated to this subnet (so the LLM can + // answer "is canister X on this subnet?"). + let canister_ranges = metadata.canister_allocation_ranges.as_ref().map(|r| { + r.ranges + .iter() + .filter_map(|range| { + let start = range + .start_canister_id + .as_ref() + .and_then(principal_to_string); + let end = range.end_canister_id.as_ref().and_then(principal_to_string); + Some(json!({"start": start?, "end": end?})) + }) + .collect::>() + }); + + Ok(json!({ + "height": height, + "own_subnet_id": own_subnet_id, + "nns_subnet_id": nns_subnet_id, + "batch_time_nanos": metadata.batch_time_nanos, + "state_sync_version": metadata.state_sync_version, + "certification_version": metadata.certification_version, + "subnets": subnets, + "canister_allocation_ranges": canister_ranges, + })) + } + + fn op_list_canisters( + &self, + height: u64, + filter: Option<&str>, + ) -> Result { + let cp = self.checkpoint_layout(height)?; + let mut ids: Vec = cp.canister_ids()?; + ids.sort(); + let ids_str: Vec = ids.into_iter().map(|c| c.to_string()).collect(); + let filtered: Vec = match filter { + Some(f) if !f.is_empty() => { + let needle = f.to_lowercase(); + ids_str + .into_iter() + .filter(|id| id.to_lowercase().contains(&needle)) + .collect() + } + _ => ids_str, + }; + Ok(json!({ + "height": height, + "count": filtered.len(), + "canister_ids": filtered, + })) + } + + fn op_canister( + &self, + height: u64, + canister_id: &str, + ) -> Result { + let cid = CanisterId::from_str(canister_id).map_err(|e| { + IcStateError::InvalidArg(format!("invalid canister_id '{canister_id}': {e}")) + })?; + let cp = self.checkpoint_layout(height)?; + let canister_layout = cp.canister(&cid)?; + if !canister_layout.raw_path().exists() { + return Err(IcStateError::NotFound(format!( + "canister {canister_id} not found at height {height}" + ))); + } + let bits: CanisterStateBits = canister_layout.canister().deserialize()?; + let cycles_balance = bits + .cycles_balance + .as_ref() + .map(|c| u128_from_le_bytes(&c.raw_cycles)); + let reserved_balance = bits + .reserved_balance + .as_ref() + .map(|c| u128_from_le_bytes(&c.raw_cycles)); + let controllers: Vec = bits + .controllers + .iter() + .filter_map(principal_to_string) + .collect(); + let module_hash_hex = bits + .execution_state_bits + .as_ref() + .map(|esb| hex::encode(&esb.binary_hash)) + .filter(|h| !h.is_empty()); + let wasm64 = bits.execution_state_bits.as_ref().map(|esb| esb.is_wasm64); + + Ok(json!({ + "height": height, + "canister_id": canister_id, + "controllers": controllers, + "cycles_balance": cycles_balance.map(|v| v.to_string()), + "reserved_balance": reserved_balance.map(|v| v.to_string()), + "freeze_threshold": bits.freeze_threshold, + "compute_allocation": bits.compute_allocation, + "memory_allocation": bits.memory_allocation, + "canister_version": bits.canister_version, + "stable_memory_size_bytes": bits.stable_memory_size64, + "module_hash": module_hash_hex, + "is_wasm64": wasm64, + })) + } +} + +/// Best-effort conversion from a protobuf `PrincipalId` (any of the IC's +/// `PrincipalId`-shaped messages share the same `raw` field) to its +/// textual form. +fn principal_to_string(p: &P) -> Option { + PrincipalId::try_from(p.raw_bytes()) + .ok() + .map(|p| p.to_string()) +} + +/// Tiny abstraction over the various protobuf PrincipalId-shaped +/// messages so we can write `principal_to_string` once. The proto +/// modules don't share a common trait so we adapt them locally. +trait HasRaw { + fn raw_bytes(&self) -> &[u8]; +} + +impl HasRaw for ic_protobuf::types::v1::PrincipalId { + fn raw_bytes(&self) -> &[u8] { + &self.raw + } +} + +impl HasRaw for ic_protobuf::types::v1::SubnetId { + fn raw_bytes(&self) -> &[u8] { + // SubnetId wraps a PrincipalId. + self.principal_id + .as_ref() + .map(|p| p.raw.as_slice()) + .unwrap_or(&[]) + } +} + +impl HasRaw for ic_protobuf::types::v1::NodeId { + fn raw_bytes(&self) -> &[u8] { + self.principal_id + .as_ref() + .map(|p| p.raw.as_slice()) + .unwrap_or(&[]) + } +} + +impl HasRaw for ic_protobuf::types::v1::CanisterId { + fn raw_bytes(&self) -> &[u8] { + self.principal_id + .as_ref() + .map(|p| p.raw.as_slice()) + .unwrap_or(&[]) + } +} + +/// Decode a little-endian byte sequence (up to 16 bytes) into a u128. +/// `Cycles.raw_cycles` is the LE encoding of the underlying u128. +fn u128_from_le_bytes(bytes: &[u8]) -> u128 { + let mut buf = [0_u8; 16]; + let n = bytes.len().min(16); + buf[..n].copy_from_slice(&bytes[..n]); + u128::from_le_bytes(buf) +} + +/// Parse `ic.json5` and return the absolute state root. +/// +/// `Config::load_with_tmpdir` needs a tmpdir to materialise some +/// derived paths internally; we don't keep it alive afterwards because +/// we only read out `state_manager.state_root()` which doesn't escape +/// it. +fn resolve_state_root(cfg_path: &Path) -> Result { + if !cfg_path.exists() { + return Err(IcStateError::Config { + path: cfg_path.to_path_buf(), + message: "file not found".to_string(), + }); + } + let tmpdir = tempfile::Builder::new() + .prefix("ic-ai-agent-cfg") + .tempdir() + .map_err(|e| IcStateError::Config { + path: cfg_path.to_path_buf(), + message: format!("failed to create tmpdir: {e}"), + })?; + let config = Config::load_with_tmpdir( + ConfigSource::File(cfg_path.to_path_buf()), + tmpdir.path().to_path_buf(), + ); + Ok(config.state_manager.state_root()) +} + +impl Tool for IcState { + const NAME: &'static str = "ic_state"; + type Error = IcStateError; + type Args = IcStateArgs; + type Output = IcStateOutput; + + async fn definition(&self, _prompt: String) -> ToolDefinition { + ToolDefinition { + name: Self::NAME.to_string(), + description: "Query the Internet Computer state for canister, subnet, or node \ + information. Use this when the operator asks about what's deployed, \ + who controls a canister, cycles balance, module hash, or node \ + membership. Does not return metrics — use `ic_metrics` for that." + .to_string(), + parameters: json!({ + "type": "object", + "properties": { + "op": { + "type": "string", + "enum": ["list_checkpoints", "subnet", "list_canisters", "canister"], + "description": + "Which view of state to return. \ + list_checkpoints: heights on disk. \ + subnet: own-subnet topology + canister id ranges. \ + list_canisters: canister ids in the latest (or specified) checkpoint. \ + canister: full per-canister state." + }, + "height": { + "type": "integer", + "minimum": 0, + "description": "Optional checkpoint height. Defaults to the latest." + }, + "filter": { + "type": "string", + "description": "Substring filter for list_canisters (case-insensitive)." + }, + "canister_id": { + "type": "string", + "description": + "Required for op=canister. Textual canister id, \ + e.g. 'rrkah-fqaaa-aaaaa-aaaaq-cai'." + } + }, + "required": ["op"] + }), + } + } + + async fn call(&self, args: Self::Args) -> Result { + let data = match args.op.as_str() { + "list_checkpoints" => { + let heights = self.list_checkpoint_heights()?; + let count = heights.len(); + json!({"heights": heights, "count": count}) + } + "subnet" => { + let h = self.resolve_height(args.height)?; + self.op_subnet(h).await? + } + "list_canisters" => { + let h = self.resolve_height(args.height)?; + self.op_list_canisters(h, args.filter.as_deref())? + } + "canister" => { + let cid = args.canister_id.ok_or_else(|| { + IcStateError::InvalidArg("canister_id is required for op=canister".to_string()) + })?; + let h = self.resolve_height(args.height)?; + self.op_canister(h, &cid)? + } + other => { + return Err(IcStateError::InvalidArg(format!( + "unknown op '{other}'; expected list_checkpoints|subnet|list_canisters|canister" + ))); + } + }; + Ok(IcStateOutput { + op: args.op, + state_root: self.state_root.display().to_string(), + data, + }) + } +} diff --git a/rs/ai_agent/src/tools/mod.rs b/rs/ai_agent/src/tools/mod.rs new file mode 100644 index 000000000000..8250bf40f3c6 --- /dev/null +++ b/rs/ai_agent/src/tools/mod.rs @@ -0,0 +1,24 @@ +//! Tool registry. +//! +//! v1 tools are statically defined Rust types implementing `rig::tool::Tool`. +//! New tools are added by creating a new sibling module and re-exporting it +//! here. The agent in `providers::AiProvider::build_agent` wires every entry +//! returned by [`registered_tool_names`] into the agent at construction +//! time. + +pub mod calculator; +pub mod current_datetime; +// `ic_logs` is currently a TODO stub — see the module docs in +// `ic_logs.rs`. Kept in the tree (not deleted) so the placeholder +// stays visible to anyone browsing the tools directory. +pub mod ic_logs; +pub mod ic_metrics; +pub mod ic_state; +pub mod node_directory; +pub mod registry; + +pub use calculator::Calculator; +pub use current_datetime::CurrentDateTime; +pub use ic_metrics::IcMetrics; +pub use ic_state::IcState; +pub use registry::{registered_tool_names, validate_tool_names}; diff --git a/rs/ai_agent/src/tools/node_directory.rs b/rs/ai_agent/src/tools/node_directory.rs new file mode 100644 index 000000000000..3a810487385a --- /dev/null +++ b/rs/ai_agent/src/tools/node_directory.rs @@ -0,0 +1,138 @@ +//! Shared helper: resolve `NodeId -> Ipv6Addr` from the local registry +//! store. +//! +//! `ic_metrics` (and, when implemented, `ic_logs`) needs to talk to a +//! peer node in the subnet that this AiNode is shadowing. The LLM +//! passes a textual node id (which it discovered via `ic_state`); we +//! look it up in the registry the orchestrator-managed state-sync +//! replica keeps on disk and return the IPv6 published in the node's +//! `NodeRecord.http`. +//! +//! Single instance per process. Holds a `RegistryClientImpl` backed by +//! a `LocalStoreImpl`. We never spawn the background polling thread — +//! the AiNode's orchestrator is the only writer, and the local store is +//! cheap to re-poll on every lookup. + +use std::{ + net::Ipv6Addr, + path::{Path, PathBuf}, + str::FromStr, + sync::Arc, +}; + +use ic_config::{Config, ConfigSource}; +use ic_interfaces_registry::RegistryClient; +use ic_registry_client::client::RegistryClientImpl; +use ic_registry_client_helpers::node::NodeRegistry; +use ic_registry_local_store::LocalStoreImpl; +use ic_types::{NodeId, PrincipalId}; + +#[derive(Debug, thiserror::Error)] +pub enum NodeDirectoryError { + #[error("failed to load replica config from {path}: {message}")] + Config { path: PathBuf, message: String }, + + #[error("registry local store not configured in {path}")] + NoLocalStore { path: PathBuf }, + + #[error("registry client error: {0:?}")] + Registry(ic_types::registry::RegistryClientError), + + #[error("invalid node id '{0}': {1}")] + InvalidNodeId(String, String), + + #[error("node {0} not found in registry at version {1}")] + NodeNotFound(NodeId, u64), + + #[error("node {0} has no http endpoint in its NodeRecord")] + NoHttpEndpoint(NodeId), + + #[error("node {0} http endpoint ip '{1}' is not a valid IPv6 address: {2}")] + BadIpv6(NodeId, String, std::net::AddrParseError), +} + +impl From for NodeDirectoryError { + fn from(e: ic_types::registry::RegistryClientError) -> Self { + NodeDirectoryError::Registry(e) + } +} + +/// Wraps a `RegistryClientImpl` driven by a local store on disk. +pub struct NodeDirectory { + client: Arc, +} + +impl NodeDirectory { + /// Build a directory backed by the registry local store referenced + /// by the given replica config. + pub fn from_ic_config(ic_config_path: &Path) -> Result { + let local_store_path = local_store_from_ic_config(ic_config_path)?; + let data_provider: Arc = + Arc::new(LocalStoreImpl::new(local_store_path)); + let client = Arc::new(RegistryClientImpl::new(data_provider, None)); + // One synchronous poll so the cache is non-empty for the very + // first lookup. Failures here are non-fatal: subsequent + // `lookup` calls will re-poll and may succeed once the + // orchestrator has populated the store. + let _ = client.poll_once(); + Ok(Self { client }) + } + + /// Resolve a textual `NodeId` to its IPv6 address. + /// + /// Re-polls the registry on every call so we pick up new nodes + /// without having to restart the agent. + pub fn resolve_ipv6(&self, node_id_str: &str) -> Result { + let principal = PrincipalId::from_str(node_id_str).map_err(|e| { + NodeDirectoryError::InvalidNodeId(node_id_str.to_string(), e.to_string()) + })?; + let node_id = NodeId::from(principal); + + // Best-effort refresh; ignore transient failures and use + // whatever's cached. + let _ = self.client.poll_once(); + + let version = self.client.get_latest_version(); + let record = self + .client + .get_node_record(node_id, version)? + .ok_or(NodeDirectoryError::NodeNotFound(node_id, version.get()))?; + let http = record + .http + .ok_or(NodeDirectoryError::NoHttpEndpoint(node_id))?; + let ip = http.ip_addr; + Ipv6Addr::from_str(&ip).map_err(|e| NodeDirectoryError::BadIpv6(node_id, ip, e)) + } +} + +/// Parse `ic.json5` and return the registry local store path. +/// +/// `Config::load_with_tmpdir` materialises some derived paths but the +/// `registry_client.local_store` field is taken verbatim from the +/// config file. +fn local_store_from_ic_config(cfg_path: &Path) -> Result { + if !cfg_path.exists() { + return Err(NodeDirectoryError::Config { + path: cfg_path.to_path_buf(), + message: "file not found".to_string(), + }); + } + let tmpdir = tempfile::Builder::new() + .prefix("ic-ai-agent-cfg") + .tempdir() + .map_err(|e| NodeDirectoryError::Config { + path: cfg_path.to_path_buf(), + message: format!("failed to create tmpdir: {e}"), + })?; + let config = Config::load_with_tmpdir( + ConfigSource::File(cfg_path.to_path_buf()), + tmpdir.path().to_path_buf(), + ); + let path = config.registry_client.local_store; + if path.as_os_str().is_empty() { + return Err(NodeDirectoryError::NoLocalStore { + path: cfg_path.to_path_buf(), + }); + } + Ok(path) +} diff --git a/rs/ai_agent/src/tools/registry.rs b/rs/ai_agent/src/tools/registry.rs new file mode 100644 index 000000000000..d4e07760bb37 --- /dev/null +++ b/rs/ai_agent/src/tools/registry.rs @@ -0,0 +1,33 @@ +//! Tool name registry. The actual tool *types* are wired into the agent +//! via [`crate::providers::AiProvider::build_agent`]; this module just +//! exposes their *names* so requests can be validated against the set of +//! supported tools. + +use rig::tool::Tool; + +use super::{Calculator, CurrentDateTime, IcMetrics, IcState}; + +/// Returns the names of all built-in tools. +/// +/// `ic_logs` is intentionally absent: the module is currently a TODO +/// stub (see `ic_logs.rs`). It will be added back here once the +/// implementation lands. +pub fn registered_tool_names() -> &'static [&'static str] { + &[ + Calculator::NAME, + CurrentDateTime::NAME, + IcState::NAME, + IcMetrics::NAME, + ] +} + +/// Validates that every name in `requested` exists in the registry. +/// Returns the first unknown name on error. +pub fn validate_tool_names(requested: &[String]) -> Result<(), String> { + for name in requested { + if !registered_tool_names().contains(&name.as_str()) { + return Err(name.clone()); + } + } + Ok(()) +} diff --git a/rs/ic_os/config/tool/templates/ic.json5.template b/rs/ic_os/config/tool/templates/ic.json5.template index 8fe7e7da4045..992e4fba6778 100644 --- a/rs/ic_os/config/tool/templates/ic.json5.template +++ b/rs/ic_os/config/tool/templates/ic.json5.template @@ -231,9 +231,11 @@ chain OUTPUT {\n\ type filter hook output priority 0; policy accept;\n\ # Allow ic-http-adapter to reach the ollama TLS reverse proxy on port\n\ - # 11434. This must come before the blanket 1-19999 reject below;\n\ - # nftables evaluates top-down, so the accept short-circuits the reject.\n\ + # 11434, and the IC AI agent TLS reverse proxy on port 11500. These\n\ + # accepts must come before the blanket 1-19999 reject below; nftables\n\ + # evaluates top-down, so the accepts short-circuit the reject.\n\ meta skuid ic-http-adapter ct state { new } tcp dport { 11434 } accept # Allow ic-http-adapter outbound to ollama (port 11434)\n\ + meta skuid ic-http-adapter ct state { new } tcp dport { 11500 } accept # Allow ic-http-adapter outbound to ic-ai-agent (port 11500)\n\ meta skuid ic-http-adapter ip daddr { 127.0.0.0/8 } ct state { new } tcp dport { 1-19999 } reject # Block restricted localhost ic-http-adapter HTTPS access\n\ <>\n\ }\n\ @@ -285,6 +287,8 @@ table ip6 filter {\n\ ip6 saddr { hostos } ct state { new } tcp dport { 42372 } accept\n\ # Allow access to the ollama HTTP API (disabled by default, started on-demand via systemctl enable --now ollama).\n\ ip6 daddr { ::/0 } ct state { new } tcp dport { 11434 } accept\n\ + # Allow access to the IC AI agent HTTP API (disabled by default, started on-demand on AI nodes).\n\ + ip6 daddr { ::/0 } ct state { new } tcp dport { 11500 } accept\n\ # Custom templated rules\n\ <>\n\ <>\n\ @@ -298,9 +302,11 @@ table ip6 filter {\n\ chain OUTPUT {\n\ type filter hook output priority 0; policy accept;\n\ # Allow ic-http-adapter to reach the ollama TLS reverse proxy on port\n\ - # 11434. This must come before the blanket 1-19999 rejects below;\n\ - # nftables evaluates top-down, so the accept short-circuits the reject.\n\ + # 11434, and the IC AI agent TLS reverse proxy on port 11500. These\n\ + # accepts must come before the blanket 1-19999 rejects below; nftables\n\ + # evaluates top-down, so the accepts short-circuit the rejects.\n\ meta skuid ic-http-adapter ct state { new } tcp dport { 11434 } accept # Allow ic-http-adapter outbound to ollama (port 11434)\n\ + meta skuid ic-http-adapter ct state { new } tcp dport { 11500 } accept # Allow ic-http-adapter outbound to ic-ai-agent (port 11500)\n\ meta skuid ic-http-adapter fib daddr type local ct state { new } tcp dport { 1-19999 } reject # Block restricted local addresses ic-http-adapter HTTPS access\n\ meta skuid ic-http-adapter ip6 daddr { 2a00:fb01:400:42::/64, 2602:fb2b:110::/48, 2602:fb2b:100::/48, 2602:fb2b:120::/48 } ct state { new } tcp dport { 1-19999 } reject # Block restricted outbound ic-http-adapter HTTPS access\n\ <>\n\ @@ -430,6 +436,8 @@ table ip6 filter {\n\ ip6 saddr { ::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff } ct state new tcp dport 443 accept\n\ # Allow access to the ollama HTTP API (disabled by default, started on-demand via systemctl enable --now ollama).\n\ ip6 daddr { ::/0 } ct state { new } tcp dport { 11434 } accept\n\ + # Allow access to the IC AI agent HTTP API (disabled by default, started on-demand on AI nodes).\n\ + ip6 daddr { ::/0 } ct state { new } tcp dport { 11500 } accept\n\ \n\ <>\n\ <>\n\ diff --git a/rs/orchestrator/testdata/nftables_assigned_cloud_engine.conf.golden b/rs/orchestrator/testdata/nftables_assigned_cloud_engine.conf.golden index 6a6a4142535b..5d0feb0e3c56 100644 --- a/rs/orchestrator/testdata/nftables_assigned_cloud_engine.conf.golden +++ b/rs/orchestrator/testdata/nftables_assigned_cloud_engine.conf.golden @@ -49,9 +49,11 @@ ip saddr {6.6.6.6} ct state { new } tcp dport {1006} accept # global chain OUTPUT { type filter hook output priority 0; policy accept; # Allow ic-http-adapter to reach the ollama TLS reverse proxy on port - # 11434. This must come before the blanket 1-19999 reject below; - # nftables evaluates top-down, so the accept short-circuits the reject. + # 11434, and the IC AI agent TLS reverse proxy on port 11500. These + # accepts must come before the blanket 1-19999 reject below; nftables + # evaluates top-down, so the accepts short-circuit the reject. meta skuid ic-http-adapter ct state { new } tcp dport { 11434 } accept # Allow ic-http-adapter outbound to ollama (port 11434) + meta skuid ic-http-adapter ct state { new } tcp dport { 11500 } accept # Allow ic-http-adapter outbound to ic-ai-agent (port 11500) meta skuid ic-http-adapter ip daddr { 127.0.0.0/8 } ct state { new } tcp dport { 1-19999 } reject # Block restricted localhost ic-http-adapter HTTPS access meta skuid ic-http-adapter ip daddr {1.1.1.1,3.0.0.3,3.0.0.4,3.0.0.5,3.0.0.6,3.0.0.7,4.0.0.4,4.0.0.5,4.0.0.6,4.0.0.7} ct state { new } tcp dport {22,2497,4100,7070,8080,9090,9091,9100,19100,19523,19531} reject # Automatic blacklisting for ic-http-adapter } @@ -103,6 +105,8 @@ table ip6 filter { ip6 saddr { hostos } ct state { new } tcp dport { 42372 } accept # Allow access to the ollama HTTP API (disabled by default, started on-demand via systemctl enable --now ollama). ip6 daddr { ::/0 } ct state { new } tcp dport { 11434 } accept + # Allow access to the IC AI agent HTTP API (disabled by default, started on-demand on AI nodes). + ip6 daddr { ::/0 } ct state { new } tcp dport { 11500 } accept # Custom templated rules ip6 saddr {2001:db8:85a3::8a2e:1370:7334,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e5,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e6,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e7,a4c2:7f91:3db6:1e8c:5a4f:cc92:b37:6e41} ct state { new } tcp dport {22,2497,4100,8080} accept # Automatic node whitelisting ip6 saddr {::ffff:5.5.5.5} ct state { new } tcp dport {1005} accept # node_gwp4o-eaaaa-aaaaa-aaaap-2ai @@ -120,9 +124,11 @@ ip6 saddr {::ffff:6.6.6.6} ct state { new } tcp dport {1006} accept # global chain OUTPUT { type filter hook output priority 0; policy accept; # Allow ic-http-adapter to reach the ollama TLS reverse proxy on port - # 11434. This must come before the blanket 1-19999 rejects below; - # nftables evaluates top-down, so the accept short-circuits the reject. + # 11434, and the IC AI agent TLS reverse proxy on port 11500. These + # accepts must come before the blanket 1-19999 rejects below; nftables + # evaluates top-down, so the accepts short-circuit the rejects. meta skuid ic-http-adapter ct state { new } tcp dport { 11434 } accept # Allow ic-http-adapter outbound to ollama (port 11434) + meta skuid ic-http-adapter ct state { new } tcp dport { 11500 } accept # Allow ic-http-adapter outbound to ic-ai-agent (port 11500) meta skuid ic-http-adapter fib daddr type local ct state { new } tcp dport { 1-19999 } reject # Block restricted local addresses ic-http-adapter HTTPS access meta skuid ic-http-adapter ip6 daddr { 2a00:fb01:400:42::/64, 2602:fb2b:110::/48, 2602:fb2b:100::/48, 2602:fb2b:120::/48 } ct state { new } tcp dport { 1-19999 } reject # Block restricted outbound ic-http-adapter HTTPS access meta skuid ic-http-adapter ip6 daddr {2001:db8:85a3::8a2e:1370:7334,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e5,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e6,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e7,a4c2:7f91:3db6:1e8c:5a4f:cc92:b37:6e41} ct state { new } tcp dport {22,2497,4100,7070,8080,9090,9091,9100,19100,19523,19531} reject # Automatic blacklisting for ic-http-adapter diff --git a/rs/orchestrator/testdata/nftables_assigned_replica.conf.golden b/rs/orchestrator/testdata/nftables_assigned_replica.conf.golden index b9b0f8fdaa9c..00b3a771f5fd 100644 --- a/rs/orchestrator/testdata/nftables_assigned_replica.conf.golden +++ b/rs/orchestrator/testdata/nftables_assigned_replica.conf.golden @@ -49,9 +49,11 @@ ip saddr {6.6.6.6} ct state { new } tcp dport {1006} accept # global chain OUTPUT { type filter hook output priority 0; policy accept; # Allow ic-http-adapter to reach the ollama TLS reverse proxy on port - # 11434. This must come before the blanket 1-19999 reject below; - # nftables evaluates top-down, so the accept short-circuits the reject. + # 11434, and the IC AI agent TLS reverse proxy on port 11500. These + # accepts must come before the blanket 1-19999 reject below; nftables + # evaluates top-down, so the accepts short-circuit the reject. meta skuid ic-http-adapter ct state { new } tcp dport { 11434 } accept # Allow ic-http-adapter outbound to ollama (port 11434) + meta skuid ic-http-adapter ct state { new } tcp dport { 11500 } accept # Allow ic-http-adapter outbound to ic-ai-agent (port 11500) meta skuid ic-http-adapter ip daddr { 127.0.0.0/8 } ct state { new } tcp dport { 1-19999 } reject # Block restricted localhost ic-http-adapter HTTPS access meta skuid ic-http-adapter ip daddr {1.1.1.1,3.0.0.3,3.0.0.4,3.0.0.5,3.0.0.6,3.0.0.7,4.0.0.4,4.0.0.5,4.0.0.6,4.0.0.7} ct state { new } tcp dport {22,2497,4100,7070,8080,9090,9091,9100,19100,19523,19531} reject # Automatic blacklisting for ic-http-adapter } @@ -103,6 +105,8 @@ table ip6 filter { ip6 saddr { hostos } ct state { new } tcp dport { 42372 } accept # Allow access to the ollama HTTP API (disabled by default, started on-demand via systemctl enable --now ollama). ip6 daddr { ::/0 } ct state { new } tcp dport { 11434 } accept + # Allow access to the IC AI agent HTTP API (disabled by default, started on-demand on AI nodes). + ip6 daddr { ::/0 } ct state { new } tcp dport { 11500 } accept # Custom templated rules ip6 saddr {3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e5,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e6,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e7,a4c2:7f91:3db6:1e8c:5a4f:cc92:b37:6e41} ct state { new } tcp dport {22,2497,4100,8080} accept # Automatic node whitelisting ip6 saddr {::ffff:5.5.5.5} ct state { new } tcp dport {1005} accept # node_gwp4o-eaaaa-aaaaa-aaaap-2ai @@ -120,9 +124,11 @@ ip6 saddr {::ffff:6.6.6.6} ct state { new } tcp dport {1006} accept # global chain OUTPUT { type filter hook output priority 0; policy accept; # Allow ic-http-adapter to reach the ollama TLS reverse proxy on port - # 11434. This must come before the blanket 1-19999 rejects below; - # nftables evaluates top-down, so the accept short-circuits the reject. + # 11434, and the IC AI agent TLS reverse proxy on port 11500. These + # accepts must come before the blanket 1-19999 rejects below; nftables + # evaluates top-down, so the accepts short-circuit the rejects. meta skuid ic-http-adapter ct state { new } tcp dport { 11434 } accept # Allow ic-http-adapter outbound to ollama (port 11434) + meta skuid ic-http-adapter ct state { new } tcp dport { 11500 } accept # Allow ic-http-adapter outbound to ic-ai-agent (port 11500) meta skuid ic-http-adapter fib daddr type local ct state { new } tcp dport { 1-19999 } reject # Block restricted local addresses ic-http-adapter HTTPS access meta skuid ic-http-adapter ip6 daddr { 2a00:fb01:400:42::/64, 2602:fb2b:110::/48, 2602:fb2b:100::/48, 2602:fb2b:120::/48 } ct state { new } tcp dport { 1-19999 } reject # Block restricted outbound ic-http-adapter HTTPS access meta skuid ic-http-adapter ip6 daddr {2001:db8:85a3::8a2e:1370:7334,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e5,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e6,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e7,a4c2:7f91:3db6:1e8c:5a4f:cc92:b37:6e41} ct state { new } tcp dport {22,2497,4100,7070,8080,9090,9091,9100,19100,19523,19531} reject # Automatic blacklisting for ic-http-adapter diff --git a/rs/orchestrator/testdata/nftables_boundary_node_app_subnet.conf.golden b/rs/orchestrator/testdata/nftables_boundary_node_app_subnet.conf.golden index 95abe290f10c..83c52e8d743d 100644 --- a/rs/orchestrator/testdata/nftables_boundary_node_app_subnet.conf.golden +++ b/rs/orchestrator/testdata/nftables_boundary_node_app_subnet.conf.golden @@ -91,6 +91,8 @@ table ip6 filter { ip6 saddr { ::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff } ct state new tcp dport 443 accept # Allow access to the ollama HTTP API (disabled by default, started on-demand via systemctl enable --now ollama). ip6 daddr { ::/0 } ct state { new } tcp dport { 11434 } accept + # Allow access to the IC AI agent HTTP API (disabled by default, started on-demand on AI nodes). + ip6 daddr { ::/0 } ct state { new } tcp dport { 11500 } accept ip6 saddr {3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e5,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e6,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e7} ct state { new } tcp dport {1080} accept # nodes for SOCKS proxy ip6 saddr {::ffff:5.5.5.5} ct state { new } tcp dport {1005} accept # node_gol4s-2gsaq-aaaaa-aaaap-2ai diff --git a/rs/orchestrator/testdata/nftables_boundary_node_system_subnet.conf.golden b/rs/orchestrator/testdata/nftables_boundary_node_system_subnet.conf.golden index 3a6e6d51ff2b..375e0fbce30a 100644 --- a/rs/orchestrator/testdata/nftables_boundary_node_system_subnet.conf.golden +++ b/rs/orchestrator/testdata/nftables_boundary_node_system_subnet.conf.golden @@ -91,6 +91,8 @@ table ip6 filter { ip6 saddr { ::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff } ct state new tcp dport 443 accept # Allow access to the ollama HTTP API (disabled by default, started on-demand via systemctl enable --now ollama). ip6 daddr { ::/0 } ct state { new } tcp dport { 11434 } accept + # Allow access to the IC AI agent HTTP API (disabled by default, started on-demand on AI nodes). + ip6 daddr { ::/0 } ct state { new } tcp dport { 11500 } accept ip6 saddr {a4c2:7f91:3db6:1e8c:5a4f:cc92:b37:6e41} ct state { new } tcp dport {1080} accept # nodes for SOCKS proxy ip6 saddr {::ffff:5.5.5.5} ct state { new } tcp dport {1005} accept # node_gwp4o-eaaaa-aaaaa-aaaap-2ai diff --git a/rs/orchestrator/testdata/nftables_unassigned_cloud_engine.conf.golden b/rs/orchestrator/testdata/nftables_unassigned_cloud_engine.conf.golden index ce84b669dd8e..545965b68bf9 100644 --- a/rs/orchestrator/testdata/nftables_unassigned_cloud_engine.conf.golden +++ b/rs/orchestrator/testdata/nftables_unassigned_cloud_engine.conf.golden @@ -48,9 +48,11 @@ ip saddr {6.6.6.6} ct state { new } tcp dport {1006} accept # global chain OUTPUT { type filter hook output priority 0; policy accept; # Allow ic-http-adapter to reach the ollama TLS reverse proxy on port - # 11434. This must come before the blanket 1-19999 reject below; - # nftables evaluates top-down, so the accept short-circuits the reject. + # 11434, and the IC AI agent TLS reverse proxy on port 11500. These + # accepts must come before the blanket 1-19999 reject below; nftables + # evaluates top-down, so the accepts short-circuit the reject. meta skuid ic-http-adapter ct state { new } tcp dport { 11434 } accept # Allow ic-http-adapter outbound to ollama (port 11434) + meta skuid ic-http-adapter ct state { new } tcp dport { 11500 } accept # Allow ic-http-adapter outbound to ic-ai-agent (port 11500) meta skuid ic-http-adapter ip daddr { 127.0.0.0/8 } ct state { new } tcp dport { 1-19999 } reject # Block restricted localhost ic-http-adapter HTTPS access meta skuid ic-http-adapter ip daddr {1.1.1.1,3.0.0.3,3.0.0.4,3.0.0.5,3.0.0.6,3.0.0.7,4.0.0.4,4.0.0.5,4.0.0.6,4.0.0.7} ct state { new } tcp dport {22,2497,4100,7070,8080,9090,9091,9100,19100,19523,19531} reject # Automatic blacklisting for ic-http-adapter } @@ -102,6 +104,8 @@ table ip6 filter { ip6 saddr { hostos } ct state { new } tcp dport { 42372 } accept # Allow access to the ollama HTTP API (disabled by default, started on-demand via systemctl enable --now ollama). ip6 daddr { ::/0 } ct state { new } tcp dport { 11434 } accept + # Allow access to the IC AI agent HTTP API (disabled by default, started on-demand on AI nodes). + ip6 daddr { ::/0 } ct state { new } tcp dport { 11500 } accept # Custom templated rules ip6 saddr {2001:db8:85a3::8a2e:1370:7334,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e5,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e6,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e7,a4c2:7f91:3db6:1e8c:5a4f:cc92:b37:6e41} ct state { new } tcp dport {22,2497,4100,8080} accept # Automatic node whitelisting ip6 saddr {::ffff:5.5.5.5} ct state { new } tcp dport {1005} accept # node_gwp4o-eaaaa-aaaaa-aaaap-2ai @@ -118,9 +122,11 @@ ip6 saddr {::ffff:6.6.6.6} ct state { new } tcp dport {1006} accept # global chain OUTPUT { type filter hook output priority 0; policy accept; # Allow ic-http-adapter to reach the ollama TLS reverse proxy on port - # 11434. This must come before the blanket 1-19999 rejects below; - # nftables evaluates top-down, so the accept short-circuits the reject. + # 11434, and the IC AI agent TLS reverse proxy on port 11500. These + # accepts must come before the blanket 1-19999 rejects below; nftables + # evaluates top-down, so the accepts short-circuit the rejects. meta skuid ic-http-adapter ct state { new } tcp dport { 11434 } accept # Allow ic-http-adapter outbound to ollama (port 11434) + meta skuid ic-http-adapter ct state { new } tcp dport { 11500 } accept # Allow ic-http-adapter outbound to ic-ai-agent (port 11500) meta skuid ic-http-adapter fib daddr type local ct state { new } tcp dport { 1-19999 } reject # Block restricted local addresses ic-http-adapter HTTPS access meta skuid ic-http-adapter ip6 daddr { 2a00:fb01:400:42::/64, 2602:fb2b:110::/48, 2602:fb2b:100::/48, 2602:fb2b:120::/48 } ct state { new } tcp dport { 1-19999 } reject # Block restricted outbound ic-http-adapter HTTPS access meta skuid ic-http-adapter ip6 daddr {2001:db8:85a3::8a2e:1370:7334,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e5,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e6,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e7,a4c2:7f91:3db6:1e8c:5a4f:cc92:b37:6e41} ct state { new } tcp dport {22,2497,4100,7070,8080,9090,9091,9100,19100,19523,19531} reject # Automatic blacklisting for ic-http-adapter diff --git a/rs/orchestrator/testdata/nftables_unassigned_replica.conf.golden b/rs/orchestrator/testdata/nftables_unassigned_replica.conf.golden index 7bd60a3dcb27..6c90bdd67189 100644 --- a/rs/orchestrator/testdata/nftables_unassigned_replica.conf.golden +++ b/rs/orchestrator/testdata/nftables_unassigned_replica.conf.golden @@ -48,9 +48,11 @@ ip saddr {6.6.6.6} ct state { new } tcp dport {1006} accept # global chain OUTPUT { type filter hook output priority 0; policy accept; # Allow ic-http-adapter to reach the ollama TLS reverse proxy on port - # 11434. This must come before the blanket 1-19999 reject below; - # nftables evaluates top-down, so the accept short-circuits the reject. + # 11434, and the IC AI agent TLS reverse proxy on port 11500. These + # accepts must come before the blanket 1-19999 reject below; nftables + # evaluates top-down, so the accepts short-circuit the reject. meta skuid ic-http-adapter ct state { new } tcp dport { 11434 } accept # Allow ic-http-adapter outbound to ollama (port 11434) + meta skuid ic-http-adapter ct state { new } tcp dport { 11500 } accept # Allow ic-http-adapter outbound to ic-ai-agent (port 11500) meta skuid ic-http-adapter ip daddr { 127.0.0.0/8 } ct state { new } tcp dport { 1-19999 } reject # Block restricted localhost ic-http-adapter HTTPS access meta skuid ic-http-adapter ip daddr {1.1.1.1,3.0.0.3,3.0.0.4,3.0.0.5,3.0.0.6,3.0.0.7,4.0.0.4,4.0.0.5,4.0.0.6,4.0.0.7} ct state { new } tcp dport {22,2497,4100,7070,8080,9090,9091,9100,19100,19523,19531} reject # Automatic blacklisting for ic-http-adapter } @@ -102,6 +104,8 @@ table ip6 filter { ip6 saddr { hostos } ct state { new } tcp dport { 42372 } accept # Allow access to the ollama HTTP API (disabled by default, started on-demand via systemctl enable --now ollama). ip6 daddr { ::/0 } ct state { new } tcp dport { 11434 } accept + # Allow access to the IC AI agent HTTP API (disabled by default, started on-demand on AI nodes). + ip6 daddr { ::/0 } ct state { new } tcp dport { 11500 } accept # Custom templated rules ip6 saddr {3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e5,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e6,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e7,a4c2:7f91:3db6:1e8c:5a4f:cc92:b37:6e41} ct state { new } tcp dport {22,2497,4100,8080} accept # Automatic node whitelisting ip6 saddr {::ffff:5.5.5.5} ct state { new } tcp dport {1005} accept # node_gwp4o-eaaaa-aaaaa-aaaap-2ai @@ -118,9 +122,11 @@ ip6 saddr {::ffff:6.6.6.6} ct state { new } tcp dport {1006} accept # global chain OUTPUT { type filter hook output priority 0; policy accept; # Allow ic-http-adapter to reach the ollama TLS reverse proxy on port - # 11434. This must come before the blanket 1-19999 rejects below; - # nftables evaluates top-down, so the accept short-circuits the reject. + # 11434, and the IC AI agent TLS reverse proxy on port 11500. These + # accepts must come before the blanket 1-19999 rejects below; nftables + # evaluates top-down, so the accepts short-circuit the rejects. meta skuid ic-http-adapter ct state { new } tcp dport { 11434 } accept # Allow ic-http-adapter outbound to ollama (port 11434) + meta skuid ic-http-adapter ct state { new } tcp dport { 11500 } accept # Allow ic-http-adapter outbound to ic-ai-agent (port 11500) meta skuid ic-http-adapter fib daddr type local ct state { new } tcp dport { 1-19999 } reject # Block restricted local addresses ic-http-adapter HTTPS access meta skuid ic-http-adapter ip6 daddr { 2a00:fb01:400:42::/64, 2602:fb2b:110::/48, 2602:fb2b:100::/48, 2602:fb2b:120::/48 } ct state { new } tcp dport { 1-19999 } reject # Block restricted outbound ic-http-adapter HTTPS access meta skuid ic-http-adapter ip6 daddr {2001:db8:85a3::8a2e:1370:7334,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e5,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e6,3fda:92b7:4c1e:8a23:7d61:2f9c:ab42:19e7,a4c2:7f91:3db6:1e8c:5a4f:cc92:b37:6e41} ct state { new } tcp dport {22,2497,4100,7070,8080,9090,9091,9100,19100,19523,19531} reject # Automatic blacklisting for ic-http-adapter diff --git a/rs/p2p/quic_transport/src/connection_manager.rs b/rs/p2p/quic_transport/src/connection_manager.rs index c55136fd8203..9806e1394de8 100644 --- a/rs/p2p/quic_transport/src/connection_manager.rs +++ b/rs/p2p/quic_transport/src/connection_manager.rs @@ -553,6 +553,13 @@ impl ConnectionManager { fn handle_inbound_conn_attemp(&mut self, incoming: Incoming) { self.metrics.inbound_connection_total.inc(); let node_id = self.node_id; + // AI-node peers always dial members, regardless of NodeId ordering + // (see `can_i_dial_to`). So when a member receives an inbound + // connection, the standard "lower id is dialer" rule must be + // bypassed if the connecting peer is an AI peer in our current + // topology. Snapshot the AI-peer set here so the validation + // future doesn't need to reach back into `&self.topology`. + let ai_peers = self.topology.get_ai_peers(); let conn_fut = async move { let established = incoming @@ -578,8 +585,13 @@ impl ConnectionManager { let peer_id = node_id_from_certificate_der(rustls_cert.as_ref()) .map_err(|err| ConnectionEstablishError::AuthenticationFailed(err.to_string()))?; - // Lower ID is dialer. So we reject if this nodes id is higher. - if peer_id > node_id { + // Standard rule: lower NodeId is dialer, higher is acceptor. + // Exception: AI-node peers always dial regardless of ordering + // because they never accept inbound from us. Without this + // bypass an AI peer with a NodeId greater than ours would + // dial us, get rejected here as `InvalidIncomingPeerId`, and + // never make progress on state-sync. + if peer_id > node_id && !ai_peers.contains(&peer_id) { return Err(ConnectionEstablishError::InvalidIncomingPeerId { client: peer_id, server: node_id,