diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index 55e05251d7..b5e91d7f22 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -100,6 +100,7 @@ jobs: - name: bazel test //... env: BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} + CODEX_BWRAP_ENABLE_FFI: ${{ contains(matrix.target, 'unknown-linux') && '1' || '0' }} shell: bash run: | bazel $BAZEL_STARTUP_ARGS --bazelrc=.github/workflows/ci.bazelrc test //... \ diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index ec6319f8da..b6a3c50e18 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -99,6 +99,9 @@ jobs: USE_SCCACHE: ${{ startsWith(matrix.runner, 'windows') && 'false' || 'true' }} CARGO_INCREMENTAL: "0" SCCACHE_CACHE_SIZE: 10G + # Keep cargo-based CI independent of system bwrap build deps. + # The bwrap FFI path is validated in Bazel workflows. + CODEX_BWRAP_ENABLE_FFI: "0" strategy: fail-fast: false @@ -467,6 +470,9 @@ jobs: USE_SCCACHE: ${{ startsWith(matrix.runner, 'windows') && 'false' || 'true' }} CARGO_INCREMENTAL: "0" SCCACHE_CACHE_SIZE: 10G + # Keep cargo-based CI independent of system bwrap build deps. + # The bwrap FFI path is validated in Bazel workflows. + CODEX_BWRAP_ENABLE_FFI: "0" strategy: fail-fast: false @@ -502,7 +508,6 @@ jobs: steps: - uses: actions/checkout@v6 - # Some integration tests rely on DotSlash being installed. # See https://github.com/openai/codex/pull/7617. - name: Install DotSlash diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 2f764df680..aa92693eee 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -65,6 +65,8 @@ jobs: defaults: run: working-directory: codex-rs + env: + CODEX_BWRAP_ENABLE_FFI: ${{ contains(matrix.target, 'unknown-linux') && '1' || '0' }} strategy: fail-fast: false @@ -89,6 +91,13 @@ jobs: steps: - uses: actions/checkout@v6 + - name: Install Linux bwrap build dependencies + if: ${{ runner.os == 'Linux' }} + shell: bash + run: | + set -euo pipefail + sudo apt-get update -y + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev - name: Install UBSan runtime (musl) if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl' }} shell: bash diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 63b7ed16ee..876bb2c57e 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -558,37 +558,41 @@ "actix-service_2.0.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"actix-rt\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"actix-utils\",\"req\":\"^3\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.17\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.17\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"}],\"features\":{}}", "actix-utils_3.0.1": "{\"dependencies\":[{\"name\":\"local-waker\",\"req\":\"^0.1\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"}],\"features\":{}}", "actix-web_4.12.1": "{\"dependencies\":[{\"name\":\"actix-codec\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"actix-files\",\"req\":\"^0.6\"},{\"name\":\"actix-http\",\"req\":\"^3.11.2\"},{\"name\":\"actix-macros\",\"optional\":true,\"req\":\"^0.2.3\"},{\"default_features\":false,\"features\":[\"http\"],\"name\":\"actix-router\",\"req\":\"^0.5.3\"},{\"default_features\":false,\"name\":\"actix-rt\",\"req\":\"^2.6\"},{\"name\":\"actix-server\",\"req\":\"^2.6\"},{\"name\":\"actix-service\",\"req\":\"^2\"},{\"features\":[\"openssl\",\"rustls-0_23\"],\"kind\":\"dev\",\"name\":\"actix-test\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"actix-tls\",\"optional\":true,\"req\":\"^3.4\"},{\"name\":\"actix-utils\",\"req\":\"^3\"},{\"default_features\":false,\"name\":\"actix-web-codegen\",\"optional\":true,\"req\":\"^4.3\"},{\"features\":[\"openssl\"],\"kind\":\"dev\",\"name\":\"awc\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"brotli\",\"req\":\"^8\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"bytestring\",\"req\":\"^1\"},{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"const-str\",\"req\":\"^0.5\"},{\"features\":[\"percent-encode\"],\"name\":\"cookie\",\"optional\":true,\"req\":\"^0.16\"},{\"kind\":\"dev\",\"name\":\"core_affinity\",\"req\":\"^0.8\"},{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"features\":[\"as_ref\",\"deref\",\"deref_mut\",\"display\",\"error\",\"from\"],\"name\":\"derive_more\",\"req\":\"^2\"},{\"name\":\"encoding_rs\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0.13\"},{\"name\":\"foldhash\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.17\"},{\"default_features\":false,\"name\":\"futures-util\",\"req\":\"^0.3.17\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.17\"},{\"name\":\"impl-more\",\"req\":\"^0.1.4\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"name\":\"language-tags\",\"req\":\"^0.3\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"mime\",\"req\":\"^0.3\"},{\"name\":\"once_cell\",\"req\":\"^1.21\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.13\"},{\"name\":\"regex\",\"optional\":true,\"req\":\"^1.5.5\"},{\"name\":\"regex-lite\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"rustls-pemfile\",\"req\":\"^2\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"serde_urlencoded\",\"req\":\"^0.7\"},{\"name\":\"smallvec\",\"req\":\"^1.6.1\"},{\"name\":\"socket2\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"formatting\"],\"name\":\"time\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"tls-openssl\",\"package\":\"openssl\",\"req\":\"^0.10.55\"},{\"kind\":\"dev\",\"name\":\"tls-rustls\",\"package\":\"rustls\",\"req\":\"^0.23\"},{\"features\":[\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.38.2\"},{\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"name\":\"tracing\",\"req\":\"^0.1.30\"},{\"name\":\"url\",\"req\":\"^2.5.4\"},{\"kind\":\"dev\",\"name\":\"zstd\",\"req\":\"^0.13\"}],\"features\":{\"__compress\":[],\"__tls\":[],\"compat\":[\"compat-routing-macros-force-pub\"],\"compat-routing-macros-force-pub\":[\"actix-web-codegen?/compat-routing-macros-force-pub\"],\"compress-brotli\":[\"actix-http/compress-brotli\",\"__compress\"],\"compress-gzip\":[\"actix-http/compress-gzip\",\"__compress\"],\"compress-zstd\":[\"actix-http/compress-zstd\",\"__compress\"],\"cookies\":[\"dep:cookie\"],\"default\":[\"macros\",\"compress-brotli\",\"compress-gzip\",\"compress-zstd\",\"cookies\",\"http2\",\"unicode\",\"compat\",\"ws\"],\"experimental-io-uring\":[\"actix-server/io-uring\"],\"http2\":[\"actix-http/http2\"],\"macros\":[\"dep:actix-macros\",\"dep:actix-web-codegen\"],\"openssl\":[\"__tls\",\"http2\",\"actix-http/openssl\",\"actix-tls/accept\",\"actix-tls/openssl\"],\"rustls\":[\"rustls-0_20\"],\"rustls-0_20\":[\"__tls\",\"http2\",\"actix-http/rustls-0_20\",\"actix-tls/accept\",\"actix-tls/rustls-0_20\"],\"rustls-0_21\":[\"__tls\",\"http2\",\"actix-http/rustls-0_21\",\"actix-tls/accept\",\"actix-tls/rustls-0_21\"],\"rustls-0_22\":[\"__tls\",\"http2\",\"actix-http/rustls-0_22\",\"actix-tls/accept\",\"actix-tls/rustls-0_22\"],\"rustls-0_23\":[\"__tls\",\"http2\",\"actix-http/rustls-0_23\",\"actix-tls/accept\",\"actix-tls/rustls-0_23\"],\"secure-cookies\":[\"cookies\",\"cookie/secure\"],\"unicode\":[\"dep:regex\",\"actix-router/unicode\"],\"ws\":[\"actix-http/ws\"]}}", - "addr2line_0.24.2": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"backtrace\",\"req\":\"^0.3.13\"},{\"features\":[\"wrap_help\"],\"name\":\"clap\",\"optional\":true,\"req\":\"^4.3.21\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.2\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"cpp_demangle\",\"optional\":true,\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"fallible-iterator\",\"optional\":true,\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"findshlibs\",\"req\":\"^0.10\"},{\"default_features\":false,\"features\":[\"read\"],\"name\":\"gimli\",\"req\":\"^0.31.1\"},{\"kind\":\"dev\",\"name\":\"libtest-mimic\",\"req\":\"^0.7.2\"},{\"name\":\"memmap2\",\"optional\":true,\"req\":\"^0.9.4\"},{\"default_features\":false,\"features\":[\"read\",\"compression\"],\"name\":\"object\",\"optional\":true,\"req\":\"^0.36.0\"},{\"name\":\"rustc-demangle\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"typed-arena\",\"optional\":true,\"req\":\"^2\"}],\"features\":{\"all\":[\"bin\"],\"bin\":[\"loader\",\"rustc-demangle\",\"cpp_demangle\",\"fallible-iterator\",\"smallvec\",\"dep:clap\"],\"cargo-all\":[],\"default\":[\"rustc-demangle\",\"cpp_demangle\",\"loader\",\"fallible-iterator\",\"smallvec\"],\"loader\":[\"std\",\"dep:object\",\"dep:memmap2\",\"dep:typed-arena\"],\"rustc-dep-of-std\":[\"core\",\"alloc\",\"compiler_builtins\",\"gimli/rustc-dep-of-std\"],\"std\":[\"gimli/std\"]}}", + "addr2line_0.25.1": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"backtrace\",\"req\":\"^0.3.13\"},{\"features\":[\"wrap_help\"],\"name\":\"clap\",\"optional\":true,\"req\":\"^4.3.21\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"cpp_demangle\",\"optional\":true,\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7.0\"},{\"default_features\":false,\"name\":\"fallible-iterator\",\"optional\":true,\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"findshlibs\",\"req\":\"^0.10\"},{\"default_features\":false,\"features\":[\"read\"],\"name\":\"gimli\",\"req\":\"^0.32.0\"},{\"kind\":\"dev\",\"name\":\"libtest-mimic\",\"req\":\"^0.8.1\"},{\"name\":\"memmap2\",\"optional\":true,\"req\":\"^0.9.4\"},{\"default_features\":false,\"features\":[\"read\",\"compression\"],\"name\":\"object\",\"optional\":true,\"req\":\"^0.37.0\"},{\"name\":\"rustc-demangle\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"typed-arena\",\"optional\":true,\"req\":\"^2\"}],\"features\":{\"all\":[\"bin\",\"wasm\"],\"bin\":[\"loader\",\"rustc-demangle\",\"cpp_demangle\",\"fallible-iterator\",\"smallvec\",\"dep:clap\"],\"cargo-all\":[],\"default\":[\"rustc-demangle\",\"cpp_demangle\",\"loader\",\"fallible-iterator\",\"smallvec\"],\"loader\":[\"std\",\"dep:object\",\"dep:memmap2\",\"dep:typed-arena\"],\"rustc-dep-of-std\":[\"core\",\"alloc\",\"gimli/rustc-dep-of-std\"],\"std\":[\"gimli/std\"],\"wasm\":[\"object/wasm\"]}}", "adler2_2.0.1": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[\"std\"],\"rustc-dep-of-std\":[\"core\"],\"std\":[]}}", + "aead_0.5.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arrayvec\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"blobby\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"crypto-common\",\"req\":\"^0.1.4\"},{\"default_features\":false,\"name\":\"generic-array\",\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"heapless\",\"optional\":true,\"req\":\"^0.7\"}],\"features\":{\"alloc\":[],\"default\":[\"rand_core\"],\"dev\":[\"blobby\"],\"getrandom\":[\"crypto-common/getrandom\",\"rand_core\"],\"rand_core\":[\"crypto-common/rand_core\"],\"std\":[\"alloc\",\"crypto-common/std\"],\"stream\":[]}}", "aes_0.8.4": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"cipher\",\"req\":\"^0.4.2\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"cipher\",\"req\":\"^0.4.2\"},{\"name\":\"cpufeatures\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_arch = \\\"aarch64\\\", target_arch = \\\"x86_64\\\", target_arch = \\\"x86\\\"))\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"aarch64\"],\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.5.6\",\"target\":\"cfg(all(aes_armv8, target_arch = \\\"aarch64\\\"))\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.6.0\",\"target\":\"cfg(not(all(aes_armv8, target_arch = \\\"aarch64\\\")))\"}],\"features\":{\"hazmat\":[]}}", + "age-core_0.11.0": "{\"dependencies\":[{\"name\":\"base64\",\"req\":\"^0.21\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"chacha20poly1305\",\"req\":\"^0.10\"},{\"name\":\"cookie-factory\",\"req\":\"^0.3.1\"},{\"name\":\"hkdf\",\"req\":\"^0.12\"},{\"name\":\"io_tee\",\"req\":\"^0.1.1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"nom\",\"req\":\"^7\"},{\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"secrecy\",\"req\":\"^0.10\"},{\"name\":\"sha2\",\"req\":\"^0.10\"},{\"name\":\"tempfile\",\"optional\":true,\"req\":\"^3.2.0\"}],\"features\":{\"plugin\":[\"tempfile\"],\"unstable\":[]}}", + "age_0.11.2": "{\"dependencies\":[{\"name\":\"aes\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"aes-gcm\",\"optional\":true,\"req\":\"^0.10\"},{\"name\":\"age-core\",\"req\":\"^0.11.0\"},{\"name\":\"base64\",\"req\":\"^0.21\"},{\"name\":\"bcrypt-pbkdf\",\"optional\":true,\"req\":\"^0.10\"},{\"name\":\"bech32\",\"req\":\"^0.9\"},{\"name\":\"cbc\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"chacha20poly1305\",\"req\":\"^0.10\"},{\"features\":[\"alloc\"],\"name\":\"cipher\",\"optional\":true,\"req\":\"^0.4.3\"},{\"default_features\":false,\"name\":\"console\",\"optional\":true,\"req\":\"^0.15\"},{\"name\":\"cookie-factory\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"criterion-cycles-per-byte\",\"req\":\"^0.6\",\"target\":\"cfg(any(target_arch = \\\"x86\\\", target_arch = \\\"x86_64\\\"))\"},{\"name\":\"ctr\",\"optional\":true,\"req\":\"^0.9\"},{\"name\":\"curve25519-dalek\",\"optional\":true,\"req\":\"^4\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"futures-test\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4\"},{\"name\":\"hmac\",\"req\":\"^0.12\"},{\"features\":[\"fluent-system\"],\"name\":\"i18n-embed\",\"req\":\"^0.15\"},{\"features\":[\"fluent-system\",\"desktop-requester\"],\"kind\":\"dev\",\"name\":\"i18n-embed\",\"req\":\"^0.15\"},{\"name\":\"i18n-embed-fl\",\"req\":\"^0.9\"},{\"name\":\"is-terminal\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.5\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"nom\",\"req\":\"^7\"},{\"name\":\"num-traits\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"pin-project\",\"req\":\"^1\"},{\"name\":\"pinentry\",\"optional\":true,\"req\":\"^0.6\"},{\"features\":[\"criterion\",\"flamegraph\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.13\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"rpassword\",\"optional\":true,\"req\":\"^7\"},{\"default_features\":false,\"name\":\"rsa\",\"optional\":true,\"req\":\"^0.9\"},{\"name\":\"rust-embed\",\"req\":\"^8\"},{\"default_features\":false,\"name\":\"scrypt\",\"req\":\"^0.11\"},{\"name\":\"sha2\",\"req\":\"^0.10\"},{\"name\":\"subtle\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"test-case\",\"req\":\"^3\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"Window\",\"Performance\"],\"name\":\"web-sys\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"which\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(any(unix, windows))\"},{\"name\":\"wsl\",\"optional\":true,\"req\":\"^0.1\",\"target\":\"cfg(any(unix, windows))\"},{\"features\":[\"static_secrets\"],\"name\":\"x25519-dalek\",\"req\":\"^2\"},{\"name\":\"zeroize\",\"req\":\"^1\"}],\"features\":{\"armor\":[],\"async\":[\"futures\",\"memchr\"],\"cli-common\":[\"console\",\"is-terminal\",\"pinentry\",\"rpassword\"],\"default\":[],\"plugin\":[\"age-core/plugin\",\"which\",\"wsl\"],\"ssh\":[\"aes\",\"aes-gcm\",\"bcrypt-pbkdf\",\"cbc\",\"cipher\",\"ctr\",\"curve25519-dalek\",\"num-traits\",\"rsa\"],\"unstable\":[\"age-core/unstable\"]}}", "ahash_0.8.12": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"const-random\",\"optional\":true,\"req\":\"^0.1.17\"},{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.2\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.5\"},{\"kind\":\"dev\",\"name\":\"fxhash\",\"req\":\"^0.2.1\"},{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"hashbrown\",\"req\":\"^0.14.3\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.2\"},{\"kind\":\"dev\",\"name\":\"no-panic\",\"req\":\"^0.1.10\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"once_cell\",\"req\":\"^1.18.0\",\"target\":\"cfg(not(all(target_arch = \\\"arm\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"pcg-mwc\",\"req\":\"^0.2.1\"},{\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"seahash\",\"req\":\"^4.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.117\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.59\"},{\"kind\":\"dev\",\"name\":\"smallvec\",\"req\":\"^1.13.1\"},{\"kind\":\"build\",\"name\":\"version_check\",\"req\":\"^0.9.4\"},{\"default_features\":false,\"features\":[\"simd\"],\"name\":\"zerocopy\",\"req\":\"^0.8.24\"}],\"features\":{\"atomic-polyfill\":[\"dep:portable-atomic\",\"once_cell/critical-section\"],\"compile-time-rng\":[\"const-random\"],\"default\":[\"std\",\"runtime-rng\"],\"nightly-arm-aes\":[],\"no-rng\":[],\"runtime-rng\":[\"getrandom\"],\"std\":[]}}", - "aho-corasick_1.1.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.3\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.17\"},{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.4.0\"}],\"features\":{\"default\":[\"std\",\"perf-literal\"],\"logging\":[\"dep:log\"],\"perf-literal\":[\"dep:memchr\"],\"std\":[\"memchr?/std\"]}}", + "aho-corasick_1.1.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.3\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.17\"},{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.4.0\"}],\"features\":{\"default\":[\"std\",\"perf-literal\"],\"logging\":[\"dep:log\"],\"perf-literal\":[\"dep:memchr\"],\"std\":[\"memchr?/std\"]}}", "allocative_0.3.4": "{\"dependencies\":[{\"name\":\"allocative_derive\",\"req\":\"=0.3.3\"},{\"name\":\"anyhow\",\"optional\":true,\"req\":\"^1.0.65\"},{\"name\":\"bumpalo\",\"optional\":true,\"req\":\"^3.11.1\"},{\"name\":\"compact_str\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"ctor\",\"req\":\"^0.1.26\"},{\"name\":\"dashmap\",\"optional\":true,\"req\":\"^5.5.3\"},{\"name\":\"either\",\"optional\":true,\"req\":\"^1.8\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.24\"},{\"features\":[\"raw\"],\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.14.3\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.2.6\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"inferno\",\"req\":\"^0.11.11\"},{\"name\":\"num-bigint\",\"optional\":true,\"req\":\"^0.4.3\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.15.0\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.11.2\"},{\"name\":\"prost-types\",\"optional\":true,\"req\":\"^0.11.2\"},{\"name\":\"relative-path\",\"optional\":true,\"req\":\"^1.7.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.48\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.7\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.10.0\"},{\"name\":\"sorted_vector_map\",\"optional\":true,\"req\":\"^0.2\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.5\"},{\"name\":\"triomphe\",\"optional\":true,\"req\":\"^0.1.8\"}],\"features\":{}}", "allocative_derive_0.3.3": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0.3\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", "allocator-api2_0.2.21": "{\"dependencies\":[{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"fresh-rust\":[],\"nightly\":[],\"std\":[\"alloc\"]}}", "android_system_properties_0.1.5": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.126\"}],\"features\":{}}", "annotate-snippets_0.9.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"difference\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"toml\",\"req\":\"^0.5\"},{\"name\":\"unicode-width\",\"req\":\"^0.1\"},{\"name\":\"yansi-term\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"yansi-term\",\"req\":\"^0.1\"}],\"features\":{\"color\":[\"yansi-term\"],\"default\":[]}}", "ansi-to-tui_7.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"nom\",\"req\":\"^7.1\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4.0\"},{\"name\":\"simdutf8\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"const_generics\"],\"name\":\"smallvec\",\"req\":\"^1.10.0\"},{\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"tui\",\"package\":\"ratatui\",\"req\":\"^0.29\"}],\"features\":{\"default\":[\"zero-copy\",\"simd\"],\"simd\":[\"dep:simdutf8\"],\"zero-copy\":[]}}", - "anstream_0.6.19": "{\"dependencies\":[{\"name\":\"anstyle\",\"req\":\"^1.0.0\"},{\"name\":\"anstyle-parse\",\"req\":\"^0.2.0\"},{\"name\":\"anstyle-query\",\"optional\":true,\"req\":\"^1.0.0\"},{\"name\":\"anstyle-wincon\",\"optional\":true,\"req\":\"^3.0.5\",\"target\":\"cfg(windows)\"},{\"name\":\"colorchoice\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"divan\",\"req\":\"^0.1.11\"},{\"name\":\"is_terminal_polyfill\",\"req\":\"^1.48\"},{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"owo-colors\",\"req\":\"^4.0.0\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.4.0\"},{\"kind\":\"dev\",\"name\":\"strip-ansi-escapes\",\"req\":\"^0.2.0\"},{\"name\":\"utf8parse\",\"req\":\"^0.2.1\"}],\"features\":{\"auto\":[\"dep:anstyle-query\"],\"default\":[\"auto\",\"wincon\"],\"test\":[],\"wincon\":[\"dep:anstyle-wincon\"]}}", + "anstream_0.6.21": "{\"dependencies\":[{\"name\":\"anstyle\",\"req\":\"^1.0.0\"},{\"name\":\"anstyle-parse\",\"req\":\"^0.2.0\"},{\"name\":\"anstyle-query\",\"optional\":true,\"req\":\"^1.0.0\"},{\"name\":\"anstyle-wincon\",\"optional\":true,\"req\":\"^3.0.5\",\"target\":\"cfg(windows)\"},{\"name\":\"colorchoice\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"divan\",\"req\":\"^0.1.16\"},{\"name\":\"is_terminal_polyfill\",\"req\":\"^1.48\"},{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"owo-colors\",\"req\":\"^4.0.0\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.7.0\"},{\"kind\":\"dev\",\"name\":\"strip-ansi-escapes\",\"req\":\"^0.2.1\"},{\"name\":\"utf8parse\",\"req\":\"^0.2.2\"}],\"features\":{\"auto\":[\"dep:anstyle-query\"],\"default\":[\"auto\",\"wincon\"],\"test\":[],\"wincon\":[\"dep:anstyle-wincon\"]}}", "anstyle-parse_0.2.7": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arrayvec\",\"optional\":true,\"req\":\"^0.7.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"codegenrs\",\"req\":\"^3.0.1\"},{\"kind\":\"dev\",\"name\":\"divan\",\"req\":\"^0.1.14\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.4.0\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.5\"},{\"name\":\"utf8parse\",\"optional\":true,\"req\":\"^0.2.1\"},{\"kind\":\"dev\",\"name\":\"vte_generate_state_changes\",\"req\":\"^0.1.1\"}],\"features\":{\"core\":[\"dep:arrayvec\"],\"default\":[\"utf8\"],\"utf8\":[\"dep:utf8parse\"]}}", - "anstyle-query_1.1.3": "{\"dependencies\":[{\"features\":[\"Win32_System_Console\",\"Win32_Foundation\"],\"name\":\"windows-sys\",\"req\":\"^0.59.0\",\"target\":\"cfg(windows)\"}],\"features\":{}}", - "anstyle-wincon_3.0.9": "{\"dependencies\":[{\"name\":\"anstyle\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.0\"},{\"name\":\"once_cell_polyfill\",\"req\":\"^1.56.0\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_System_Console\",\"Win32_Foundation\"],\"name\":\"windows-sys\",\"req\":\"^0.59.0\",\"target\":\"cfg(windows)\"}],\"features\":{}}", - "anstyle_1.0.11": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "anstyle-query_1.1.5": "{\"dependencies\":[{\"features\":[\"Win32_System_Console\",\"Win32_Foundation\"],\"name\":\"windows-sys\",\"req\":\">=0.60.2, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{}}", + "anstyle-wincon_3.0.11": "{\"dependencies\":[{\"name\":\"anstyle\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.1\"},{\"name\":\"once_cell_polyfill\",\"req\":\"^1.56.1\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_System_Console\",\"Win32_Foundation\"],\"name\":\"windows-sys\",\"req\":\">=0.60.2, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{}}", + "anstyle_1.0.13": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.5\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "anyhow_1.0.100": "{\"dependencies\":[{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.51\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.6\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "arbitrary_1.4.2": "{\"dependencies\":[{\"name\":\"derive_arbitrary\",\"optional\":true,\"req\":\"~1.4.0\"},{\"kind\":\"dev\",\"name\":\"exhaustigen\",\"req\":\"^0.1.0\"}],\"features\":{\"derive\":[\"derive_arbitrary\"]}}", "arboard_3.6.1": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"clipboard-win\",\"req\":\"^5.3.1\",\"target\":\"cfg(windows)\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10.2\"},{\"default_features\":false,\"features\":[\"png\"],\"name\":\"image\",\"optional\":true,\"req\":\"^0.25\",\"target\":\"cfg(all(unix, not(any(target_os=\\\"macos\\\", target_os=\\\"android\\\", target_os=\\\"emscripten\\\"))))\"},{\"default_features\":false,\"features\":[\"tiff\"],\"name\":\"image\",\"optional\":true,\"req\":\"^0.25\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"default_features\":false,\"features\":[\"png\",\"bmp\"],\"name\":\"image\",\"optional\":true,\"req\":\"^0.25\",\"target\":\"cfg(windows)\"},{\"name\":\"log\",\"req\":\"^0.4\",\"target\":\"cfg(all(unix, not(any(target_os=\\\"macos\\\", target_os=\\\"android\\\", target_os=\\\"emscripten\\\"))))\"},{\"name\":\"log\",\"req\":\"^0.4\",\"target\":\"cfg(windows)\"},{\"name\":\"objc2\",\"req\":\"^0.6.0\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"default_features\":false,\"features\":[\"std\",\"objc2-core-graphics\",\"NSPasteboard\",\"NSPasteboardItem\",\"NSImage\"],\"name\":\"objc2-app-kit\",\"req\":\"^0.3.0\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"default_features\":false,\"features\":[\"std\",\"CFCGTypes\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.0\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"default_features\":false,\"features\":[\"std\",\"CGImage\",\"CGColorSpace\",\"CGDataProvider\"],\"name\":\"objc2-core-graphics\",\"optional\":true,\"req\":\"^0.3.0\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"default_features\":false,\"features\":[\"std\",\"NSArray\",\"NSString\",\"NSEnumerator\",\"NSGeometry\",\"NSValue\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.0\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"name\":\"parking_lot\",\"req\":\"^0.12\",\"target\":\"cfg(all(unix, not(any(target_os=\\\"macos\\\", target_os=\\\"android\\\", target_os=\\\"emscripten\\\"))))\"},{\"name\":\"percent-encoding\",\"req\":\"^2.3.1\",\"target\":\"cfg(all(unix, not(any(target_os=\\\"macos\\\", target_os=\\\"android\\\", target_os=\\\"emscripten\\\"))))\"},{\"features\":[\"Win32_Foundation\",\"Win32_Storage_FileSystem\",\"Win32_System_DataExchange\",\"Win32_System_Memory\",\"Win32_System_Ole\",\"Win32_UI_Shell\"],\"name\":\"windows-sys\",\"req\":\">=0.52.0, <0.61.0\",\"target\":\"cfg(windows)\"},{\"name\":\"wl-clipboard-rs\",\"optional\":true,\"req\":\"^0.9.0\",\"target\":\"cfg(all(unix, not(any(target_os=\\\"macos\\\", target_os=\\\"android\\\", target_os=\\\"emscripten\\\"))))\"},{\"name\":\"x11rb\",\"req\":\"^0.13\",\"target\":\"cfg(all(unix, not(any(target_os=\\\"macos\\\", target_os=\\\"android\\\", target_os=\\\"emscripten\\\"))))\"}],\"features\":{\"core-graphics\":[\"dep:objc2-core-graphics\"],\"default\":[\"image-data\"],\"image\":[\"dep:image\"],\"image-data\":[\"dep:objc2-core-graphics\",\"dep:objc2-core-foundation\",\"image\",\"windows-sys\",\"core-graphics\"],\"wayland-data-control\":[\"wl-clipboard-rs\"],\"windows-sys\":[\"windows-sys/Win32_Graphics_Gdi\"],\"wl-clipboard-rs\":[\"dep:wl-clipboard-rs\"]}}", "arc-swap_1.8.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"adaptive-barrier\",\"req\":\"~1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"~0.7\"},{\"kind\":\"dev\",\"name\":\"crossbeam-utils\",\"req\":\"~0.8\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.14\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"~1\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"~1\"},{\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"~0.12\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"name\":\"rustversion\",\"req\":\"^1\"},{\"features\":[\"rc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.130\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.177\"}],\"features\":{\"experimental-strategies\":[],\"experimental-thread-local\":[],\"internal-test-strategies\":[],\"weak\":[]}}", "arrayvec_0.7.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.4\"},{\"default_features\":false,\"name\":\"borsh\",\"optional\":true,\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"matches\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.4\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "ascii-canvas_3.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"diff\",\"req\":\"^0.1\"},{\"name\":\"term\",\"req\":\"^0.7\"}],\"features\":{}}", "ascii_1.1.0": "{\"dependencies\":[{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.25\"},{\"name\":\"serde_test\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "assert-json-diff_2.0.2": "{\"dependencies\":[{\"name\":\"serde\",\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"version-sync\",\"req\":\"^0.8\"}],\"features\":{}}", - "assert_cmd_2.0.17": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.7\"},{\"name\":\"anstyle\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"},{\"name\":\"bstr\",\"req\":\"^1.0.1\"},{\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"escargot\",\"req\":\"^0.5\"},{\"name\":\"libc\",\"req\":\"^0.2.137\",\"target\":\"cfg(any())\"},{\"default_features\":false,\"features\":[\"diff\"],\"name\":\"predicates\",\"req\":\"^3.0.1\"},{\"name\":\"predicates-core\",\"req\":\"^1.0.6\"},{\"name\":\"predicates-tree\",\"req\":\"^1.0.1\"},{\"name\":\"wait-timeout\",\"req\":\"^0.2.0\"}],\"features\":{\"color\":[\"dep:anstream\",\"predicates/color\"],\"color-auto\":[\"color\"]}}", + "assert_cmd_2.1.2": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.7\"},{\"name\":\"anstyle\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"},{\"name\":\"bstr\",\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"escargot\",\"req\":\"^0.5\"},{\"name\":\"libc\",\"req\":\"^0.2.137\",\"target\":\"cfg(any())\"},{\"default_features\":false,\"features\":[\"diff\"],\"name\":\"predicates\",\"req\":\"^3.0.1\"},{\"name\":\"predicates-core\",\"req\":\"^1.0.6\"},{\"name\":\"predicates-tree\",\"req\":\"^1.0.1\"},{\"name\":\"wait-timeout\",\"req\":\"^0.2.0\"}],\"features\":{\"color\":[\"dep:anstream\",\"predicates/color\"],\"color-auto\":[\"color\"]}}", "assert_matches_1.5.0": "{\"dependencies\":[],\"features\":{}}", "async-broadcast_0.7.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.5\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.3\"},{\"kind\":\"dev\",\"name\":\"easy-parallel\",\"req\":\"^3.2.0\"},{\"name\":\"event-listener\",\"req\":\"^5.0.0\"},{\"name\":\"event-listener-strategy\",\"req\":\"^0.5.0\"},{\"name\":\"futures-core\",\"req\":\"^0.3.21\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^1.11.3\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.21\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.13\"}],\"features\":{}}", "async-channel_2.5.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"concurrent-queue\",\"req\":\"^2.5\"},{\"kind\":\"dev\",\"name\":\"easy-parallel\",\"req\":\"^3\"},{\"default_features\":false,\"name\":\"event-listener-strategy\",\"req\":\"^0.5.4\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.5\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"default_features\":false,\"features\":[\"require-cas\"],\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"portable-atomic-util\",\"optional\":true,\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.37\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"portable-atomic\":[\"concurrent-queue/portable-atomic\",\"event-listener-strategy/portable-atomic\",\"dep:portable-atomic-util\",\"dep:portable-atomic\"],\"std\":[\"concurrent-queue/std\",\"event-listener-strategy/std\"]}}", "async-executor_1.13.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-channel\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"async-io\",\"req\":\"^2.1.0\"},{\"kind\":\"dev\",\"name\":\"async-lock\",\"req\":\"^3.0.0\"},{\"name\":\"async-task\",\"req\":\"^4.4.0\"},{\"name\":\"concurrent-queue\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"easy-parallel\",\"req\":\"^3.1.0\"},{\"name\":\"fastrand\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2.0.0\"},{\"default_features\":false,\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-lite\",\"req\":\"^2.0.0\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.16.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"name\":\"slab\",\"req\":\"^0.4.7\"}],\"features\":{\"static\":[]}}", "async-fs_2.2.0": "{\"dependencies\":[{\"name\":\"async-lock\",\"req\":\"^3.0.0\"},{\"name\":\"blocking\",\"req\":\"^1.3.0\"},{\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.78\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Storage_FileSystem\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "async-io_2.6.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-channel\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"async-net\",\"req\":\"^2.0.0\"},{\"kind\":\"build\",\"name\":\"autocfg\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"blocking\",\"req\":\"^1\"},{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"concurrent-queue\",\"req\":\"^2.2.0\"},{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-io\",\"req\":\"^0.3.28\"},{\"default_features\":false,\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"inotify\",\"req\":\"^0.11.0\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"parking\",\"req\":\"^2.0.0\"},{\"name\":\"polling\",\"req\":\"^3.4.0\"},{\"default_features\":false,\"features\":[\"fs\",\"net\",\"std\"],\"name\":\"rustix\",\"req\":\"^1.0.7\"},{\"kind\":\"dev\",\"name\":\"signal-hook\",\"req\":\"^0.3\"},{\"name\":\"slab\",\"req\":\"^0.4.2\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"timerfd\",\"req\":\"^1\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.37\"},{\"kind\":\"dev\",\"name\":\"uds_windows\",\"req\":\"^1\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_Foundation\"],\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{}}", - "async-lock_3.4.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"event-listener\",\"req\":\"^5.0.0\"},{\"default_features\":false,\"name\":\"event-listener-strategy\",\"req\":\"^0.5.0\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"flume\",\"req\":\"^0.11.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"kind\":\"dev\",\"name\":\"waker-fn\",\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"loom\":[\"event-listener/loom\",\"dep:loom\"],\"std\":[\"event-listener/std\",\"event-listener-strategy/std\"]}}", + "async-lock_3.4.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"event-listener\",\"req\":\"^5.0.0\"},{\"default_features\":false,\"name\":\"event-listener-strategy\",\"req\":\"^0.5.0\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"flume\",\"req\":\"^0.12.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"kind\":\"dev\",\"name\":\"waker-fn\",\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"loom\":[\"event-listener/loom\",\"dep:loom\"],\"std\":[\"event-listener/std\",\"event-listener-strategy/std\"]}}", "async-process_2.5.0": "{\"dependencies\":[{\"name\":\"async-channel\",\"req\":\"^2.0.0\",\"target\":\"cfg(any(windows, target_os = \\\"linux\\\"))\"},{\"kind\":\"dev\",\"name\":\"async-executor\",\"req\":\"^1.5.1\"},{\"name\":\"async-io\",\"req\":\"^2.3.0\"},{\"name\":\"async-lock\",\"req\":\"^3.0.0\",\"target\":\"cfg(unix)\"},{\"name\":\"async-signal\",\"req\":\"^0.2.3\",\"target\":\"cfg(unix)\"},{\"name\":\"async-task\",\"req\":\"^4.7.0\",\"target\":\"cfg(any(windows, target_os = \\\"linux\\\"))\"},{\"name\":\"blocking\",\"req\":\"^1.0.0\",\"target\":\"cfg(windows)\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"event-listener\",\"req\":\"^5.1.0\"},{\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"default_features\":false,\"features\":[\"std\",\"fs\",\"process\"],\"name\":\"rustix\",\"req\":\"^1.0\",\"target\":\"cfg(unix)\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.40\"},{\"default_features\":false,\"features\":[\"Win32_Foundation\",\"Win32_System_Threading\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "async-recursion_1.1.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures-executor\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"macrotest\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"full\",\"visit-mut\",\"parsing\",\"printing\",\"proc-macro\",\"clone-impls\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"}],\"features\":{}}", "async-signal_0.2.13": "{\"dependencies\":[{\"name\":\"async-io\",\"req\":\"^2.0.0\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"async-io\",\"req\":\"^2.0.0\"},{\"name\":\"async-lock\",\"req\":\"^3.3.0\",\"target\":\"cfg(windows)\"},{\"name\":\"atomic-waker\",\"req\":\"^1.1.1\",\"target\":\"cfg(windows)\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2.0.1\"},{\"name\":\"futures-core\",\"req\":\"^0.3.26\"},{\"name\":\"futures-io\",\"req\":\"^0.3.26\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.3.0\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.139\",\"target\":\"cfg(unix)\"},{\"default_features\":false,\"features\":[\"process\",\"std\"],\"name\":\"rustix\",\"req\":\"^1.0.7\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"signal-hook\",\"req\":\"^0.3.14\"},{\"name\":\"signal-hook-registry\",\"req\":\"^1.4.0\",\"target\":\"cfg(unix)\"},{\"name\":\"slab\",\"req\":\"^0.4.8\",\"target\":\"cfg(windows)\"},{\"default_features\":false,\"features\":[\"Win32_Foundation\",\"Win32_System_Console\"],\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{}}", @@ -602,9 +606,12 @@ "autocfg_1.5.0": "{\"dependencies\":[],\"features\":{}}", "axum-core_0.5.6": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"name\":\"http\",\"req\":\"^1.0.0\"},{\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.0.0\"},{\"name\":\"mime\",\"req\":\"^0.3.16\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"name\":\"sync_wrapper\",\"req\":\"^1.0.0\"},{\"features\":[\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.25.0\"},{\"features\":[\"limit\"],\"name\":\"tower-http\",\"optional\":true,\"req\":\"^0.6.0\"},{\"features\":[\"limit\"],\"kind\":\"dev\",\"name\":\"tower-http\",\"req\":\"^0.6.0\"},{\"name\":\"tower-layer\",\"req\":\"^0.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.37\"}],\"features\":{\"__private_docs\":[\"dep:tower-http\"],\"tracing\":[\"dep:tracing\"]}}", "axum_0.8.8": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"axum-core\",\"req\":\"^0.5.5\"},{\"name\":\"axum-macros\",\"optional\":true,\"req\":\"^0.5.0\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22.1\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"name\":\"form_urlencoded\",\"optional\":true,\"req\":\"^1.1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"http\",\"req\":\"^1.0.0\"},{\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"name\":\"hyper\",\"optional\":true,\"req\":\"^1.1.0\"},{\"features\":[\"client\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.1.0\"},{\"features\":[\"tokio\",\"server\",\"service\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1.3\"},{\"name\":\"itoa\",\"req\":\"^1.0.5\"},{\"name\":\"matchit\",\"req\":\"=0.8.4\"},{\"name\":\"memchr\",\"req\":\"^2.4.1\"},{\"name\":\"mime\",\"req\":\"^0.3.16\"},{\"name\":\"multer\",\"optional\":true,\"req\":\"^3.0.0\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"json\",\"stream\",\"multipart\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"default_features\":false,\"features\":[\"json\",\"stream\",\"multipart\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.211\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.221\"},{\"name\":\"serde_core\",\"req\":\"^1.0.221\"},{\"features\":[\"raw_value\"],\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"raw_value\"],\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"serde_path_to_error\",\"optional\":true,\"req\":\"^0.1.8\"},{\"name\":\"serde_urlencoded\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"sha1\",\"optional\":true,\"req\":\"^0.10\"},{\"name\":\"sync_wrapper\",\"req\":\"^1.0.0\"},{\"features\":[\"serde-human-readable\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3\"},{\"features\":[\"time\"],\"name\":\"tokio\",\"optional\":true,\"package\":\"tokio\",\"req\":\"^1.44\"},{\"features\":[\"macros\",\"rt\",\"rt-multi-thread\",\"net\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"package\":\"tokio\",\"req\":\"^1.44.2\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"name\":\"tokio-tungstenite\",\"optional\":true,\"req\":\"^0.28.0\"},{\"kind\":\"dev\",\"name\":\"tokio-tungstenite\",\"req\":\"^0.28.0\"},{\"default_features\":false,\"features\":[\"util\"],\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"features\":[\"util\",\"timeout\",\"limit\",\"load-shed\",\"steer\",\"filter\"],\"kind\":\"dev\",\"name\":\"tower\",\"package\":\"tower\",\"req\":\"^0.5.2\"},{\"features\":[\"add-extension\",\"auth\",\"catch-panic\",\"compression-br\",\"compression-deflate\",\"compression-gzip\",\"cors\",\"decompression-br\",\"decompression-deflate\",\"decompression-gzip\",\"follow-redirect\",\"fs\",\"limit\",\"map-request-body\",\"map-response-body\",\"metrics\",\"normalize-path\",\"propagate-header\",\"redirect\",\"request-id\",\"sensitive-headers\",\"set-header\",\"set-status\",\"timeout\",\"trace\",\"util\",\"validate-request\"],\"name\":\"tower-http\",\"optional\":true,\"req\":\"^0.6.0\"},{\"features\":[\"add-extension\",\"auth\",\"catch-panic\",\"compression-br\",\"compression-deflate\",\"compression-gzip\",\"cors\",\"decompression-br\",\"decompression-deflate\",\"decompression-gzip\",\"follow-redirect\",\"fs\",\"limit\",\"map-request-body\",\"map-response-body\",\"metrics\",\"normalize-path\",\"propagate-header\",\"redirect\",\"request-id\",\"sensitive-headers\",\"set-header\",\"set-status\",\"timeout\",\"trace\",\"util\",\"validate-request\"],\"kind\":\"dev\",\"name\":\"tower-http\",\"req\":\"^0.6.0\"},{\"name\":\"tower-layer\",\"req\":\"^0.3.2\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"features\":[\"serde\",\"v4\"],\"kind\":\"dev\",\"name\":\"uuid\",\"req\":\"^1.0\"}],\"features\":{\"__private\":[\"tokio\",\"http1\",\"dep:reqwest\"],\"__private_docs\":[\"axum-core/__private_docs\",\"tower/full\",\"dep:serde\",\"dep:tower-http\"],\"default\":[\"form\",\"http1\",\"json\",\"matched-path\",\"original-uri\",\"query\",\"tokio\",\"tower-log\",\"tracing\"],\"form\":[\"dep:form_urlencoded\",\"dep:serde_urlencoded\",\"dep:serde_path_to_error\"],\"http1\":[\"dep:hyper\",\"hyper?/http1\",\"hyper-util?/http1\"],\"http2\":[\"dep:hyper\",\"hyper?/http2\",\"hyper-util?/http2\"],\"json\":[\"dep:serde_json\",\"dep:serde_path_to_error\"],\"macros\":[\"dep:axum-macros\"],\"matched-path\":[],\"multipart\":[\"dep:multer\"],\"original-uri\":[],\"query\":[\"dep:form_urlencoded\",\"dep:serde_urlencoded\",\"dep:serde_path_to_error\"],\"tokio\":[\"dep:hyper-util\",\"dep:tokio\",\"tokio/net\",\"tokio/rt\",\"tower/make\",\"tokio/macros\"],\"tower-log\":[\"tower/log\"],\"tracing\":[\"dep:tracing\",\"axum-core/tracing\"],\"ws\":[\"dep:hyper\",\"tokio\",\"dep:tokio-tungstenite\",\"dep:sha1\",\"dep:base64\"]}}", - "backtrace_0.3.75": "{\"dependencies\":[{\"default_features\":false,\"name\":\"addr2line\",\"req\":\"^0.24.0\",\"target\":\"cfg(not(all(windows, target_env = \\\"msvc\\\", not(target_vendor = \\\"uwp\\\"))))\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"cpp_demangle\",\"optional\":true,\"req\":\"^0.4.0\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.156\",\"target\":\"cfg(not(all(windows, target_env = \\\"msvc\\\", not(target_vendor = \\\"uwp\\\"))))\"},{\"kind\":\"dev\",\"name\":\"libloading\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"miniz_oxide\",\"req\":\"^0.8\",\"target\":\"cfg(not(all(windows, target_env = \\\"msvc\\\", not(target_vendor = \\\"uwp\\\"))))\"},{\"default_features\":false,\"features\":[\"read_core\",\"elf\",\"macho\",\"pe\",\"xcoff\",\"unaligned\",\"archive\"],\"name\":\"object\",\"req\":\"^0.36.0\",\"target\":\"cfg(not(all(windows, target_env = \\\"msvc\\\", not(target_vendor = \\\"uwp\\\"))))\"},{\"name\":\"rustc-demangle\",\"req\":\"^0.1.24\"},{\"default_features\":false,\"name\":\"ruzstd\",\"optional\":true,\"req\":\"^0.7.3\",\"target\":\"cfg(not(all(windows, target_env = \\\"msvc\\\", not(target_vendor = \\\"uwp\\\"))))\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"windows-targets\",\"req\":\"^0.52.6\",\"target\":\"cfg(any(windows, target_os = \\\"cygwin\\\"))\"}],\"features\":{\"coresymbolication\":[],\"dbghelp\":[],\"default\":[\"std\"],\"dl_iterate_phdr\":[],\"dladdr\":[],\"kernel32\":[],\"libunwind\":[],\"ruzstd\":[\"dep:ruzstd\"],\"serialize-serde\":[\"serde\"],\"std\":[],\"unix-backtrace\":[]}}", + "backtrace_0.3.76": "{\"dependencies\":[{\"default_features\":false,\"name\":\"addr2line\",\"req\":\"^0.25.0\",\"target\":\"cfg(not(all(windows, target_env = \\\"msvc\\\", not(target_vendor = \\\"uwp\\\"))))\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"cpp_demangle\",\"optional\":true,\"req\":\"^0.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.156\",\"target\":\"cfg(not(all(windows, target_env = \\\"msvc\\\", not(target_vendor = \\\"uwp\\\"))))\"},{\"kind\":\"dev\",\"name\":\"libloading\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"miniz_oxide\",\"req\":\"^0.8\",\"target\":\"cfg(not(all(windows, target_env = \\\"msvc\\\", not(target_vendor = \\\"uwp\\\"))))\"},{\"default_features\":false,\"features\":[\"read_core\",\"elf\",\"macho\",\"pe\",\"xcoff\",\"unaligned\",\"archive\"],\"name\":\"object\",\"req\":\"^0.37.0\",\"target\":\"cfg(not(all(windows, target_env = \\\"msvc\\\", not(target_vendor = \\\"uwp\\\"))))\"},{\"name\":\"rustc-demangle\",\"req\":\"^0.1.24\"},{\"default_features\":false,\"name\":\"ruzstd\",\"optional\":true,\"req\":\"^0.8.1\",\"target\":\"cfg(not(all(windows, target_env = \\\"msvc\\\", not(target_vendor = \\\"uwp\\\"))))\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"windows-link\",\"req\":\"^0.2\",\"target\":\"cfg(any(windows, target_os = \\\"cygwin\\\"))\"}],\"features\":{\"coresymbolication\":[],\"dbghelp\":[],\"default\":[\"std\"],\"dl_iterate_phdr\":[],\"dladdr\":[],\"kernel32\":[],\"libunwind\":[],\"ruzstd\":[\"dep:ruzstd\"],\"serialize-serde\":[\"serde\"],\"std\":[],\"unix-backtrace\":[]}}", + "base64_0.21.7": "{\"dependencies\":[{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^3.2.25\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.13.0\"},{\"kind\":\"dev\",\"name\":\"rstest_reuse\",\"req\":\"^0.6.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"strum\",\"req\":\"^0.25\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "base64_0.22.1": "{\"dependencies\":[{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^3.2.25\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.13.0\"},{\"kind\":\"dev\",\"name\":\"rstest_reuse\",\"req\":\"^0.6.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"strum\",\"req\":\"^0.25\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", - "base64ct_1.8.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"base64\",\"req\":\"^0.22\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.6\"}],\"features\":{\"alloc\":[],\"std\":[\"alloc\"]}}", + "base64ct_1.8.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"base64\",\"req\":\"^0.22\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.6\"}],\"features\":{\"alloc\":[],\"std\":[\"alloc\"]}}", + "basic-toml_0.1.10": "{\"dependencies\":[{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"semver\",\"req\":\"^1.0.17\"},{\"name\":\"serde\",\"req\":\"^1.0.194\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.194\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.194\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.99\"}],\"features\":{}}", + "bech32_0.9.1": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[],\"strict\":[]}}", "beef_0.5.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.105\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.105\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"const_fn\":[],\"default\":[],\"impl_serde\":[\"serde\"]}}", "bindgen_0.72.1": "{\"dependencies\":[{\"name\":\"annotate-snippets\",\"optional\":true,\"req\":\"^0.11.4\"},{\"name\":\"bitflags\",\"req\":\"^2.2.1\"},{\"name\":\"cexpr\",\"req\":\"^0.6\"},{\"features\":[\"clang_11_0\"],\"name\":\"clang-sys\",\"req\":\"^1\"},{\"features\":[\"derive\"],\"name\":\"clap\",\"optional\":true,\"req\":\"^4\"},{\"name\":\"clap_complete\",\"optional\":true,\"req\":\"^4\"},{\"default_features\":false,\"name\":\"itertools\",\"req\":\">=0.10, <0.14\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"features\":[\"verbatim\"],\"name\":\"prettyplease\",\"optional\":true,\"req\":\"^0.2.7\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.80\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\",\"unicode-perl\"],\"name\":\"regex\",\"req\":\"^1.5.3\"},{\"name\":\"rustc-hash\",\"req\":\"^2.1.0\"},{\"name\":\"shlex\",\"req\":\"^1\"},{\"features\":[\"full\",\"extra-traits\",\"visit-mut\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{\"__cli\":[\"dep:clap\",\"dep:clap_complete\"],\"__testing_only_extra_assertions\":[],\"__testing_only_libclang_16\":[],\"__testing_only_libclang_9\":[],\"default\":[\"logging\",\"prettyplease\",\"runtime\"],\"experimental\":[\"dep:annotate-snippets\"],\"logging\":[\"dep:log\"],\"runtime\":[\"clang-sys/runtime\"],\"static\":[\"clang-sys/static\"]}}", "bit-set_0.5.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bit-vec\",\"req\":\"^0.6.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.3\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"bit-vec/std\"]}}", @@ -613,34 +620,39 @@ "bitflags_2.10.0": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"arbitrary\",\"req\":\"^1.0\"},{\"name\":\"bytemuck\",\"optional\":true,\"req\":\"^1.12\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.12.2\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.228\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde_lib\",\"package\":\"serde\",\"req\":\"^1.0.103\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.19\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.18\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"zerocopy\",\"req\":\"^0.8\"}],\"features\":{\"example_generated\":[],\"serde\":[\"serde_core\"],\"std\":[]}}", "block-buffer_0.10.4": "{\"dependencies\":[{\"name\":\"generic-array\",\"req\":\"^0.14\"}],\"features\":{}}", "block-padding_0.3.3": "{\"dependencies\":[{\"name\":\"generic-array\",\"req\":\"^0.14\"}],\"features\":{\"std\":[]}}", + "block2_0.6.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"}],\"features\":{\"alloc\":[],\"compiler-rt\":[\"objc2/unstable-compiler-rt\"],\"default\":[\"std\"],\"gnustep-1-7\":[\"objc2/gnustep-1-7\"],\"gnustep-1-8\":[\"gnustep-1-7\",\"objc2/gnustep-1-8\"],\"gnustep-1-9\":[\"gnustep-1-8\",\"objc2/gnustep-1-9\"],\"gnustep-2-0\":[\"gnustep-1-9\",\"objc2/gnustep-2-0\"],\"gnustep-2-1\":[\"gnustep-2-0\",\"objc2/gnustep-2-1\"],\"std\":[\"alloc\"],\"unstable-coerce-pointee\":[],\"unstable-objfw\":[],\"unstable-private\":[],\"unstable-winobjc\":[\"gnustep-1-8\"]}}", "blocking_1.6.2": "{\"dependencies\":[{\"name\":\"async-channel\",\"req\":\"^2.0.0\"},{\"name\":\"async-task\",\"req\":\"^4.4.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-io\",\"req\":\"^0.3.28\"},{\"default_features\":false,\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"piper\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.37\"}],\"features\":{}}", "borsh_1.6.0": "{\"dependencies\":[{\"name\":\"ascii\",\"optional\":true,\"req\":\"^1.1\"},{\"name\":\"borsh-derive\",\"optional\":true,\"req\":\"~1.6.0\"},{\"name\":\"bson\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"build\",\"name\":\"cfg_aliases\",\"req\":\"^0.2.1\"},{\"name\":\"hashbrown\",\"optional\":true,\"req\":\">=0.11, <0.16.0\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.29.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"}],\"features\":{\"de_strict_order\":[],\"default\":[\"std\"],\"derive\":[\"borsh-derive\"],\"rc\":[],\"std\":[],\"unstable__schema\":[\"derive\",\"borsh-derive/schema\"]}}", - "bstr_1.12.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2.7.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"dfa-search\"],\"name\":\"regex-automata\",\"optional\":true,\"req\":\"^0.4.1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.85\"},{\"kind\":\"dev\",\"name\":\"ucd-parse\",\"req\":\"^0.1.3\"},{\"kind\":\"dev\",\"name\":\"unicode-segmentation\",\"req\":\"^1.2.1\"}],\"features\":{\"alloc\":[\"memchr/alloc\",\"serde?/alloc\"],\"default\":[\"std\",\"unicode\"],\"serde\":[\"dep:serde\"],\"std\":[\"alloc\",\"memchr/std\",\"serde?/std\"],\"unicode\":[\"dep:regex-automata\"]}}", - "bumpalo_3.19.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"allocator-api2\",\"optional\":true,\"req\":\"^0.2.8\"},{\"kind\":\"dev\",\"name\":\"blink-alloc\",\"req\":\"=0.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.171\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.197\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.115\"}],\"features\":{\"allocator_api\":[],\"bench_allocator_api\":[\"allocator_api\",\"blink-alloc/nightly\"],\"boxed\":[],\"collections\":[],\"default\":[],\"serde\":[\"dep:serde\"],\"std\":[]}}", - "bytemuck_1.23.1": "{\"dependencies\":[{\"name\":\"bytemuck_derive\",\"optional\":true,\"req\":\"^1.4.1\"}],\"features\":{\"aarch64_simd\":[],\"align_offset\":[],\"alloc_uninit\":[],\"avx512_simd\":[],\"const_zeroed\":[],\"derive\":[\"bytemuck_derive\"],\"extern_crate_alloc\":[],\"extern_crate_std\":[\"extern_crate_alloc\"],\"impl_core_error\":[],\"latest_stable_rust\":[\"aarch64_simd\",\"avx512_simd\",\"align_offset\",\"alloc_uninit\",\"const_zeroed\",\"derive\",\"impl_core_error\",\"min_const_generics\",\"must_cast\",\"must_cast_extra\",\"pod_saturating\",\"track_caller\",\"transparentwrapper_extra\",\"wasm_simd\",\"zeroable_atomics\",\"zeroable_maybe_uninit\",\"zeroable_unwind_fn\"],\"min_const_generics\":[],\"must_cast\":[],\"must_cast_extra\":[\"must_cast\"],\"nightly_docs\":[],\"nightly_float\":[],\"nightly_portable_simd\":[],\"nightly_stdsimd\":[],\"pod_saturating\":[],\"track_caller\":[],\"transparentwrapper_extra\":[],\"unsound_ptr_pod_impl\":[],\"wasm_simd\":[],\"zeroable_atomics\":[],\"zeroable_maybe_uninit\":[],\"zeroable_unwind_fn\":[]}}", + "bstr_1.12.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2.7.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"dfa-search\"],\"name\":\"regex-automata\",\"optional\":true,\"req\":\"^0.4.1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.85\"},{\"kind\":\"dev\",\"name\":\"ucd-parse\",\"req\":\"^0.1.3\"},{\"kind\":\"dev\",\"name\":\"unicode-segmentation\",\"req\":\"^1.2.1\"}],\"features\":{\"alloc\":[\"memchr/alloc\",\"serde?/alloc\"],\"default\":[\"std\",\"unicode\"],\"serde\":[\"dep:serde\"],\"std\":[\"alloc\",\"memchr/std\",\"serde?/std\"],\"unicode\":[\"dep:regex-automata\"]}}", + "bumpalo_3.19.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"allocator-api2\",\"optional\":true,\"req\":\"^0.2.8\"},{\"kind\":\"dev\",\"name\":\"blink-alloc\",\"req\":\"=0.4.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"=1.10.0\"},{\"kind\":\"dev\",\"name\":\"rayon-core\",\"req\":\"=1.12.1\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.171\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.197\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.115\"}],\"features\":{\"allocator_api\":[],\"bench_allocator_api\":[\"allocator_api\",\"blink-alloc/nightly\"],\"boxed\":[],\"collections\":[],\"default\":[],\"serde\":[\"dep:serde\"],\"std\":[]}}", + "bytemuck_1.25.0": "{\"dependencies\":[{\"name\":\"bytemuck_derive\",\"optional\":true,\"req\":\"^1.10.2\"},{\"name\":\"rustversion\",\"optional\":true,\"req\":\"^1.0.22\"}],\"features\":{\"aarch64_simd\":[],\"align_offset\":[],\"alloc_uninit\":[],\"avx512_simd\":[],\"const_zeroed\":[],\"derive\":[\"bytemuck_derive\"],\"extern_crate_alloc\":[],\"extern_crate_std\":[\"extern_crate_alloc\"],\"impl_core_error\":[],\"latest_stable_rust\":[\"aarch64_simd\",\"avx512_simd\",\"align_offset\",\"alloc_uninit\",\"const_zeroed\",\"derive\",\"impl_core_error\",\"min_const_generics\",\"must_cast\",\"must_cast_extra\",\"pod_saturating\",\"track_caller\",\"transparentwrapper_extra\",\"wasm_simd\",\"zeroable_atomics\",\"zeroable_maybe_uninit\",\"zeroable_unwind_fn\"],\"min_const_generics\":[],\"must_cast\":[],\"must_cast_extra\":[\"must_cast\"],\"nightly_docs\":[],\"nightly_float\":[],\"nightly_portable_simd\":[\"rustversion\"],\"nightly_stdsimd\":[],\"pod_saturating\":[],\"track_caller\":[],\"transparentwrapper_extra\":[],\"unsound_ptr_pod_impl\":[],\"wasm_simd\":[],\"zeroable_atomics\":[],\"zeroable_maybe_uninit\":[],\"zeroable_unwind_fn\":[]}}", "byteorder-lite_0.1.0": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^0.9.2\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.7\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "byteorder_1.5.0": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^0.9.2\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.7\"}],\"features\":{\"default\":[\"std\"],\"i128\":[],\"std\":[]}}", - "bytes_1.10.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"require-cas\"],\"name\":\"extra-platforms\",\"optional\":true,\"package\":\"portable-atomic\",\"req\":\"^1.3\"},{\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.60\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "bytes_1.11.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"require-cas\"],\"name\":\"extra-platforms\",\"optional\":true,\"package\":\"portable-atomic\",\"req\":\"^1.3\"},{\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.60\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "bytestring_1.5.0": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"ahash\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"bytes\",\"req\":\"^1.2\"},{\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"}],\"features\":{\"serde\":[\"dep:serde_core\"]}}", + "bzip2-sys_0.1.13+1.0.8": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.9\"}],\"features\":{\"__disabled\":[],\"static\":[]}}", + "bzip2_0.5.2": "{\"dependencies\":[{\"name\":\"bzip2-sys\",\"optional\":true,\"req\":\"^0.1.13\"},{\"default_features\":false,\"features\":[\"rust-allocator\",\"semver-prefix\"],\"name\":\"libbz2-rs-sys\",\"optional\":true,\"req\":\"^0.1.3\"},{\"features\":[\"quickcheck1\"],\"kind\":\"dev\",\"name\":\"partial-io\",\"req\":\"^0.5.4\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"}],\"features\":{\"default\":[\"dep:bzip2-sys\"],\"libbz2-rs-sys\":[\"dep:libbz2-rs-sys\",\"bzip2-sys?/__disabled\"],\"static\":[\"bzip2-sys?/static\"]}}", "cassowary_0.3.0": "{\"dependencies\":[],\"features\":{}}", "castaway_0.2.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1\"},{\"name\":\"rustversion\",\"req\":\"^1\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "cbc_0.1.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"aes\",\"req\":\"^0.8\"},{\"name\":\"cipher\",\"req\":\"^0.4.2\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"cipher\",\"req\":\"^0.4.2\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3.3\"}],\"features\":{\"alloc\":[\"cipher/alloc\"],\"block-padding\":[\"cipher/block-padding\"],\"default\":[\"block-padding\"],\"std\":[\"cipher/std\",\"alloc\"],\"zeroize\":[\"cipher/zeroize\"]}}", - "cc_1.2.52": "{\"dependencies\":[{\"name\":\"find-msvc-tools\",\"req\":\"^0.1.7\"},{\"default_features\":false,\"name\":\"jobserver\",\"optional\":true,\"req\":\"^0.1.30\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.62\",\"target\":\"cfg(unix)\"},{\"name\":\"shlex\",\"req\":\"^1.3.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"jobserver\":[],\"parallel\":[\"dep:libc\",\"dep:jobserver\"]}}", + "cc_1.2.55": "{\"dependencies\":[{\"name\":\"find-msvc-tools\",\"req\":\"^0.1.9\"},{\"default_features\":false,\"name\":\"jobserver\",\"optional\":true,\"req\":\"^0.1.30\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.62\",\"target\":\"cfg(unix)\"},{\"name\":\"shlex\",\"req\":\"^1.3.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"jobserver\":[],\"parallel\":[\"dep:libc\",\"dep:jobserver\"]}}", "cesu8_1.1.0": "{\"dependencies\":[],\"features\":{\"unstable\":[]}}", "cexpr_0.6.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"clang-sys\",\"req\":\">=0.13.0, <0.29.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"nom\",\"req\":\"^7\"}],\"features\":{}}", - "cfg-if_1.0.1": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"rustc-dep-of-std\":[\"core\"]}}", + "cfg-if_1.0.4": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"rustc-dep-of-std\":[\"core\"]}}", "cfg_aliases_0.1.1": "{\"dependencies\":[],\"features\":{}}", "cfg_aliases_0.2.1": "{\"dependencies\":[],\"features\":{}}", + "chacha20_0.9.1": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"cipher\",\"req\":\"^0.4.4\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"cipher\",\"req\":\"^0.4.4\"},{\"name\":\"cpufeatures\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_arch = \\\"x86_64\\\", target_arch = \\\"x86\\\"))\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3.3\"}],\"features\":{\"std\":[\"cipher/std\"],\"zeroize\":[\"cipher/zeroize\"]}}", + "chacha20poly1305_0.10.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"aead\",\"req\":\"^0.5\"},{\"default_features\":false,\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"aead\",\"req\":\"^0.5\"},{\"features\":[\"zeroize\"],\"name\":\"chacha20\",\"req\":\"^0.9\"},{\"name\":\"cipher\",\"req\":\"^0.4\"},{\"name\":\"poly1305\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"zeroize\",\"req\":\"^1.5\"}],\"features\":{\"alloc\":[\"aead/alloc\"],\"default\":[\"alloc\",\"getrandom\"],\"getrandom\":[\"aead/getrandom\",\"rand_core\"],\"heapless\":[\"aead/heapless\"],\"rand_core\":[\"aead/rand_core\"],\"reduced-round\":[],\"std\":[\"aead/std\",\"alloc\"],\"stream\":[\"aead/stream\"]}}", "chardetng_0.1.17": "{\"dependencies\":[{\"name\":\"arrayvec\",\"optional\":true,\"req\":\"^0.5.1\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"detone\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"encoding_rs\",\"req\":\"^0.8.29\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2.2.0\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.3.0\"}],\"features\":{\"multithreading\":[\"rayon\",\"arrayvec\"],\"testing-only-no-semver-guarantees-do-not-use\":[]}}", "chrono_0.4.43": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.0\"},{\"name\":\"defmt\",\"optional\":true,\"req\":\"^1.0.1\"},{\"features\":[\"fallback\"],\"name\":\"iana-time-zone\",\"optional\":true,\"req\":\"^0.1.45\",\"target\":\"cfg(unix)\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"default_features\":false,\"name\":\"num-traits\",\"req\":\"^0.2\"},{\"name\":\"pure-rust-locales\",\"optional\":true,\"req\":\"^0.8.2\"},{\"default_features\":false,\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.7.43\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.99\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"similar-asserts\",\"req\":\"^1.6.1\"},{\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"windows-bindgen\",\"req\":\"^0.66\"},{\"name\":\"windows-link\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(windows)\"}],\"features\":{\"__internal_bench\":[],\"alloc\":[],\"clock\":[\"winapi\",\"iana-time-zone\",\"now\"],\"core-error\":[],\"default\":[\"clock\",\"std\",\"oldtime\",\"wasmbind\"],\"defmt\":[\"dep:defmt\",\"pure-rust-locales?/defmt\"],\"libc\":[],\"now\":[\"std\"],\"oldtime\":[],\"rkyv\":[\"dep:rkyv\",\"rkyv/size_32\"],\"rkyv-16\":[\"dep:rkyv\",\"rkyv?/size_16\"],\"rkyv-32\":[\"dep:rkyv\",\"rkyv?/size_32\"],\"rkyv-64\":[\"dep:rkyv\",\"rkyv?/size_64\"],\"rkyv-validation\":[\"rkyv?/validation\"],\"std\":[\"alloc\"],\"unstable-locales\":[\"pure-rust-locales\"],\"wasmbind\":[\"wasm-bindgen\",\"js-sys\"],\"winapi\":[\"windows-link\"]}}", "chunked_transfer_1.5.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"}],\"features\":{}}", "cipher_0.4.4": "{\"dependencies\":[{\"name\":\"blobby\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"crypto-common\",\"req\":\"^0.1.6\"},{\"name\":\"inout\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.5\"}],\"features\":{\"alloc\":[],\"block-padding\":[\"inout/block-padding\"],\"dev\":[\"blobby\"],\"rand_core\":[\"crypto-common/rand_core\"],\"std\":[\"alloc\",\"crypto-common/std\",\"inout/std\"]}}", "clang-sys_1.8.1": "{\"dependencies\":[{\"name\":\"glob\",\"req\":\"^0.3\"},{\"kind\":\"build\",\"name\":\"glob\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.39\"},{\"name\":\"libloading\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\">=3.0.0, <3.7.0\"}],\"features\":{\"clang_10_0\":[\"clang_9_0\"],\"clang_11_0\":[\"clang_10_0\"],\"clang_12_0\":[\"clang_11_0\"],\"clang_13_0\":[\"clang_12_0\"],\"clang_14_0\":[\"clang_13_0\"],\"clang_15_0\":[\"clang_14_0\"],\"clang_16_0\":[\"clang_15_0\"],\"clang_17_0\":[\"clang_16_0\"],\"clang_18_0\":[\"clang_17_0\"],\"clang_3_5\":[],\"clang_3_6\":[\"clang_3_5\"],\"clang_3_7\":[\"clang_3_6\"],\"clang_3_8\":[\"clang_3_7\"],\"clang_3_9\":[\"clang_3_8\"],\"clang_4_0\":[\"clang_3_9\"],\"clang_5_0\":[\"clang_4_0\"],\"clang_6_0\":[\"clang_5_0\"],\"clang_7_0\":[\"clang_6_0\"],\"clang_8_0\":[\"clang_7_0\"],\"clang_9_0\":[\"clang_8_0\"],\"libcpp\":[],\"runtime\":[\"libloading\"],\"static\":[]}}", - "clap_4.5.54": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"clap-cargo\",\"req\":\"^0.15.0\"},{\"default_features\":false,\"name\":\"clap_builder\",\"req\":\"=4.5.54\"},{\"name\":\"clap_derive\",\"optional\":true,\"req\":\"=4.5.49\"},{\"kind\":\"dev\",\"name\":\"jiff\",\"req\":\"^0.2.3\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.15\"},{\"kind\":\"dev\",\"name\":\"semver\",\"req\":\"^1.0.26\"},{\"kind\":\"dev\",\"name\":\"shlex\",\"req\":\"^1.3.0\"},{\"features\":[\"term-svg\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.16\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.91\"},{\"default_features\":false,\"features\":[\"color-auto\",\"diff\",\"examples\"],\"kind\":\"dev\",\"name\":\"trycmd\",\"req\":\"^0.15.3\"}],\"features\":{\"cargo\":[\"clap_builder/cargo\"],\"color\":[\"clap_builder/color\"],\"debug\":[\"clap_builder/debug\",\"clap_derive?/debug\"],\"default\":[\"std\",\"color\",\"help\",\"usage\",\"error-context\",\"suggestions\"],\"deprecated\":[\"clap_builder/deprecated\",\"clap_derive?/deprecated\"],\"derive\":[\"dep:clap_derive\"],\"env\":[\"clap_builder/env\"],\"error-context\":[\"clap_builder/error-context\"],\"help\":[\"clap_builder/help\"],\"std\":[\"clap_builder/std\"],\"string\":[\"clap_builder/string\"],\"suggestions\":[\"clap_builder/suggestions\"],\"unicode\":[\"clap_builder/unicode\"],\"unstable-derive-ui-tests\":[],\"unstable-doc\":[\"clap_builder/unstable-doc\",\"derive\"],\"unstable-ext\":[\"clap_builder/unstable-ext\"],\"unstable-markdown\":[\"clap_derive/unstable-markdown\"],\"unstable-styles\":[\"clap_builder/unstable-styles\"],\"unstable-v5\":[\"clap_builder/unstable-v5\",\"clap_derive?/unstable-v5\",\"deprecated\"],\"usage\":[\"clap_builder/usage\"],\"wrap_help\":[\"clap_builder/wrap_help\"]}}", - "clap_builder_4.5.54": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.7\"},{\"name\":\"anstyle\",\"req\":\"^1.0.8\"},{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.73\"},{\"name\":\"clap_lex\",\"req\":\"^0.7.4\"},{\"kind\":\"dev\",\"name\":\"color-print\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.16\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.0\"},{\"name\":\"terminal_size\",\"optional\":true,\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"unic-emoji-char\",\"req\":\"^0.9.0\"},{\"name\":\"unicase\",\"optional\":true,\"req\":\"^2.6.0\"},{\"name\":\"unicode-width\",\"optional\":true,\"req\":\"^0.2.0\"}],\"features\":{\"cargo\":[],\"color\":[\"dep:anstream\"],\"debug\":[\"dep:backtrace\"],\"default\":[\"std\",\"color\",\"help\",\"usage\",\"error-context\",\"suggestions\"],\"deprecated\":[],\"env\":[],\"error-context\":[],\"help\":[],\"std\":[\"anstyle/std\"],\"string\":[],\"suggestions\":[\"dep:strsim\",\"error-context\"],\"unicode\":[\"dep:unicode-width\",\"dep:unicase\"],\"unstable-doc\":[\"cargo\",\"wrap_help\",\"env\",\"unicode\",\"string\",\"unstable-ext\"],\"unstable-ext\":[],\"unstable-styles\":[\"color\"],\"unstable-v5\":[\"deprecated\"],\"usage\":[],\"wrap_help\":[\"help\",\"dep:terminal_size\"]}}", - "clap_complete_4.5.64": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"clap\",\"req\":\"^4.5.20\"},{\"default_features\":false,\"features\":[\"std\",\"derive\",\"help\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.5.20\"},{\"name\":\"clap_lex\",\"optional\":true,\"req\":\"^0.7.0\"},{\"name\":\"completest\",\"optional\":true,\"req\":\"^0.4.2\"},{\"name\":\"completest-pty\",\"optional\":true,\"req\":\"^0.5.5\"},{\"name\":\"is_executable\",\"optional\":true,\"req\":\"^1.0.1\"},{\"name\":\"shlex\",\"optional\":true,\"req\":\"^1.3.0\"},{\"features\":[\"diff\",\"dir\",\"examples\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.0\"},{\"default_features\":false,\"features\":[\"color-auto\",\"diff\",\"examples\"],\"kind\":\"dev\",\"name\":\"trycmd\",\"req\":\"^0.15.1\"}],\"features\":{\"debug\":[\"clap/debug\"],\"default\":[],\"unstable-doc\":[\"unstable-dynamic\"],\"unstable-dynamic\":[\"dep:clap_lex\",\"dep:shlex\",\"dep:is_executable\",\"clap/unstable-ext\"],\"unstable-shell-tests\":[\"dep:completest\",\"dep:completest-pty\"]}}", - "clap_derive_4.5.49": "{\"dependencies\":[{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.10\"},{\"name\":\"heck\",\"req\":\"^0.5.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.69\"},{\"default_features\":false,\"name\":\"pulldown-cmark\",\"optional\":true,\"req\":\"^0.13.0\"},{\"name\":\"quote\",\"req\":\"^1.0.9\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.8\"}],\"features\":{\"debug\":[],\"default\":[],\"deprecated\":[],\"raw-deprecated\":[\"deprecated\"],\"unstable-markdown\":[\"dep:pulldown-cmark\",\"dep:anstyle\"],\"unstable-v5\":[\"deprecated\"]}}", - "clap_lex_0.7.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"}],\"features\":{}}", + "clap_4.5.56": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"clap-cargo\",\"req\":\"^0.15.0\"},{\"default_features\":false,\"name\":\"clap_builder\",\"req\":\"=4.5.56\"},{\"name\":\"clap_derive\",\"optional\":true,\"req\":\"=4.5.55\"},{\"kind\":\"dev\",\"name\":\"jiff\",\"req\":\"^0.2.3\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.15\"},{\"kind\":\"dev\",\"name\":\"semver\",\"req\":\"^1.0.26\"},{\"kind\":\"dev\",\"name\":\"shlex\",\"req\":\"^1.3.0\"},{\"features\":[\"term-svg\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.16\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.91\"},{\"default_features\":false,\"features\":[\"color-auto\",\"diff\",\"examples\"],\"kind\":\"dev\",\"name\":\"trycmd\",\"req\":\"^0.15.3\"}],\"features\":{\"cargo\":[\"clap_builder/cargo\"],\"color\":[\"clap_builder/color\"],\"debug\":[\"clap_builder/debug\",\"clap_derive?/debug\"],\"default\":[\"std\",\"color\",\"help\",\"usage\",\"error-context\",\"suggestions\"],\"deprecated\":[\"clap_builder/deprecated\",\"clap_derive?/deprecated\"],\"derive\":[\"dep:clap_derive\"],\"env\":[\"clap_builder/env\"],\"error-context\":[\"clap_builder/error-context\"],\"help\":[\"clap_builder/help\"],\"std\":[\"clap_builder/std\"],\"string\":[\"clap_builder/string\"],\"suggestions\":[\"clap_builder/suggestions\"],\"unicode\":[\"clap_builder/unicode\"],\"unstable-derive-ui-tests\":[],\"unstable-doc\":[\"clap_builder/unstable-doc\",\"derive\"],\"unstable-ext\":[\"clap_builder/unstable-ext\"],\"unstable-markdown\":[\"clap_derive/unstable-markdown\"],\"unstable-styles\":[\"clap_builder/unstable-styles\"],\"unstable-v5\":[\"clap_builder/unstable-v5\",\"clap_derive?/unstable-v5\",\"deprecated\"],\"usage\":[\"clap_builder/usage\"],\"wrap_help\":[\"clap_builder/wrap_help\"]}}", + "clap_builder_4.5.56": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.7\"},{\"name\":\"anstyle\",\"req\":\"^1.0.8\"},{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.73\"},{\"name\":\"clap_lex\",\"req\":\"^0.7.4\"},{\"kind\":\"dev\",\"name\":\"color-print\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.16\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.0\"},{\"name\":\"terminal_size\",\"optional\":true,\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"unic-emoji-char\",\"req\":\"^0.9.0\"},{\"name\":\"unicase\",\"optional\":true,\"req\":\"^2.6.0\"},{\"name\":\"unicode-width\",\"optional\":true,\"req\":\"^0.2.0\"}],\"features\":{\"cargo\":[],\"color\":[\"dep:anstream\"],\"debug\":[\"dep:backtrace\"],\"default\":[\"std\",\"color\",\"help\",\"usage\",\"error-context\",\"suggestions\"],\"deprecated\":[],\"env\":[],\"error-context\":[],\"help\":[],\"std\":[\"anstyle/std\"],\"string\":[],\"suggestions\":[\"dep:strsim\",\"error-context\"],\"unicode\":[\"dep:unicode-width\",\"dep:unicase\"],\"unstable-doc\":[\"cargo\",\"wrap_help\",\"env\",\"unicode\",\"string\",\"unstable-ext\"],\"unstable-ext\":[],\"unstable-styles\":[\"color\"],\"unstable-v5\":[\"deprecated\"],\"usage\":[],\"wrap_help\":[\"help\",\"dep:terminal_size\"]}}", + "clap_complete_4.5.65": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"clap\",\"req\":\"^4.5.20\"},{\"default_features\":false,\"features\":[\"std\",\"derive\",\"help\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.5.20\"},{\"name\":\"clap_lex\",\"optional\":true,\"req\":\"^0.7.0\"},{\"name\":\"completest\",\"optional\":true,\"req\":\"^0.4.2\"},{\"name\":\"completest-pty\",\"optional\":true,\"req\":\"^0.5.5\"},{\"name\":\"is_executable\",\"optional\":true,\"req\":\"^1.0.1\"},{\"name\":\"shlex\",\"optional\":true,\"req\":\"^1.3.0\"},{\"features\":[\"diff\",\"dir\",\"examples\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.0\"},{\"default_features\":false,\"features\":[\"color-auto\",\"diff\",\"examples\"],\"kind\":\"dev\",\"name\":\"trycmd\",\"req\":\"^0.15.1\"}],\"features\":{\"debug\":[\"clap/debug\"],\"default\":[],\"unstable-doc\":[\"unstable-dynamic\"],\"unstable-dynamic\":[\"dep:clap_lex\",\"dep:shlex\",\"dep:is_executable\",\"clap/unstable-ext\"],\"unstable-shell-tests\":[\"dep:completest\",\"dep:completest-pty\"]}}", + "clap_derive_4.5.55": "{\"dependencies\":[{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.10\"},{\"name\":\"heck\",\"req\":\"^0.5.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.69\"},{\"default_features\":false,\"name\":\"pulldown-cmark\",\"optional\":true,\"req\":\"^0.13.0\"},{\"name\":\"quote\",\"req\":\"^1.0.9\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.8\"}],\"features\":{\"debug\":[],\"default\":[],\"deprecated\":[],\"raw-deprecated\":[\"deprecated\"],\"unstable-markdown\":[\"dep:pulldown-cmark\",\"dep:anstyle\"],\"unstable-v5\":[\"deprecated\"]}}", + "clap_lex_0.7.7": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"}],\"features\":{}}", "clipboard-win_5.4.1": "{\"dependencies\":[{\"name\":\"error-code\",\"req\":\"^3\",\"target\":\"cfg(windows)\"},{\"name\":\"windows-win\",\"optional\":true,\"req\":\"^3\",\"target\":\"cfg(windows)\"}],\"features\":{\"monitor\":[\"windows-win\"],\"std\":[\"error-code/std\"]}}", "cmake_0.1.57": "{\"dependencies\":[{\"name\":\"cc\",\"req\":\"^1.2.46\"}],\"features\":{}}", "cmp_any_0.8.1": "{\"dependencies\":[],\"features\":{}}", @@ -655,8 +667,10 @@ "const-oid_0.9.6": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3\"}],\"features\":{\"db\":[],\"std\":[]}}", "const_format_0.2.35": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"arrayvec\",\"req\":\"^0.7.0\"},{\"name\":\"const_format_proc_macros\",\"req\":\"=0.2.34\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^1.3.5\"},{\"default_features\":false,\"name\":\"konst\",\"optional\":true,\"req\":\"^0.2.13\"}],\"features\":{\"__debug\":[\"const_format_proc_macros/debug\"],\"__docsrs\":[],\"__inline_const_pat_tests\":[\"__test\",\"fmt\"],\"__only_new_tests\":[\"__test\"],\"__test\":[],\"all\":[\"fmt\",\"derive\",\"rust_1_64\",\"assert\"],\"assert\":[\"assertc\"],\"assertc\":[\"fmt\",\"assertcp\"],\"assertcp\":[\"rust_1_51\"],\"const_generics\":[\"rust_1_51\"],\"constant_time_as_str\":[\"fmt\"],\"default\":[],\"derive\":[\"fmt\",\"const_format_proc_macros/derive\"],\"fmt\":[\"rust_1_83\"],\"more_str_macros\":[\"rust_1_64\"],\"nightly_const_generics\":[\"const_generics\"],\"rust_1_51\":[],\"rust_1_64\":[\"rust_1_51\",\"konst\",\"konst/rust_1_64\"],\"rust_1_83\":[\"rust_1_64\"]}}", "const_format_proc_macros_0.2.34": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^1.3.4\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.19\"},{\"name\":\"quote\",\"req\":\"^1.0.7\"},{\"default_features\":false,\"features\":[\"parsing\",\"proc-macro\"],\"name\":\"syn\",\"optional\":true,\"req\":\"^1.0.38\"},{\"name\":\"unicode-xid\",\"req\":\"^0.2\"}],\"features\":{\"all\":[\"derive\"],\"debug\":[\"syn/extra-traits\"],\"default\":[],\"derive\":[\"syn\",\"syn/derive\",\"syn/printing\"]}}", + "constant_time_eq_0.3.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"count_instructions\",\"req\":\"^0.1.3\"},{\"features\":[\"cargo_bench_support\",\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"}],\"features\":{\"count_instructions_test\":[]}}", "convert_case_0.10.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"unicode-segmentation\",\"req\":\"^1.9.0\"}],\"features\":{}}", "convert_case_0.6.0": "{\"dependencies\":[{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"strum\",\"req\":\"^0.18.0\"},{\"kind\":\"dev\",\"name\":\"strum_macros\",\"req\":\"^0.18.0\"},{\"name\":\"unicode-segmentation\",\"req\":\"^1.9.0\"}],\"features\":{\"random\":[\"rand\"]}}", + "cookie-factory_0.3.3": "{\"dependencies\":[{\"features\":[\"attributes\"],\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.9.0\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.16\"},{\"kind\":\"dev\",\"name\":\"maplit\",\"req\":\"^1.0\"}],\"features\":{\"async\":[\"futures\"],\"default\":[\"std\",\"async\"],\"std\":[]}}", "core-foundation-sys_0.8.7": "{\"dependencies\":[],\"features\":{\"default\":[\"link\"],\"link\":[],\"mac_os_10_7_support\":[],\"mac_os_10_8_features\":[]}}", "core-foundation_0.10.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"core-foundation-sys\",\"req\":\"^0.8\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"default\":[\"link\"],\"link\":[\"core-foundation-sys/link\"],\"mac_os_10_7_support\":[\"core-foundation-sys/mac_os_10_7_support\"],\"mac_os_10_8_features\":[\"core-foundation-sys/mac_os_10_8_features\"],\"with-uuid\":[\"dep:uuid\"]}}", "core-foundation_0.9.4": "{\"dependencies\":[{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"core-foundation-sys\",\"req\":\"^0.8.6\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^0.5\"}],\"features\":{\"default\":[\"link\"],\"link\":[\"core-foundation-sys/link\"],\"mac_os_10_7_support\":[\"core-foundation-sys/mac_os_10_7_support\"],\"mac_os_10_8_features\":[\"core-foundation-sys/mac_os_10_8_features\"],\"with-chrono\":[\"chrono\"],\"with-uuid\":[\"uuid\"]}}", @@ -672,31 +686,32 @@ "crossbeam-utils_0.8.21": "{\"dependencies\":[{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7.1\",\"target\":\"cfg(crossbeam_loom)\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"}],\"features\":{\"default\":[\"std\"],\"nightly\":[],\"std\":[]}}", "crossterm_winapi_0.9.1": "{\"dependencies\":[{\"features\":[\"winbase\",\"consoleapi\",\"processenv\",\"handleapi\",\"synchapi\",\"impl-default\"],\"name\":\"winapi\",\"req\":\"^0.3.8\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "crunchy_0.2.4": "{\"dependencies\":[],\"features\":{\"default\":[\"limit_128\"],\"limit_1024\":[],\"limit_128\":[],\"limit_2048\":[],\"limit_256\":[],\"limit_512\":[],\"limit_64\":[],\"std\":[]}}", - "crypto-common_0.1.6": "{\"dependencies\":[{\"features\":[\"more_lengths\"],\"name\":\"generic-array\",\"req\":\"^0.14.4\"},{\"name\":\"rand_core\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"typenum\",\"req\":\"^1.14\"}],\"features\":{\"getrandom\":[\"rand_core/getrandom\"],\"std\":[]}}", + "crypto-common_0.1.7": "{\"dependencies\":[{\"features\":[\"more_lengths\"],\"name\":\"generic-array\",\"req\":\"=0.14.7\"},{\"name\":\"rand_core\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"typenum\",\"req\":\"^1.14\"}],\"features\":{\"getrandom\":[\"rand_core/getrandom\"],\"std\":[]}}", "csv-core_0.1.13": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"arrayvec\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2\"}],\"features\":{\"default\":[],\"libc\":[\"memchr/libc\"]}}", "csv_1.4.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\",\"serde\"],\"kind\":\"dev\",\"name\":\"bstr\",\"req\":\"^1.7.0\"},{\"name\":\"csv-core\",\"req\":\"^0.1.11\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"name\":\"ryu\",\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.221\"},{\"name\":\"serde_core\",\"req\":\"^1.0.221\"}],\"features\":{}}", "ctor-proc-macro_0.0.7": "{\"dependencies\":[],\"features\":{\"default\":[]}}", "ctor_0.1.26": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"libc-print\",\"req\":\"^0.1.20\"},{\"name\":\"quote\",\"req\":\"^1.0.20\"},{\"default_features\":false,\"features\":[\"full\",\"parsing\",\"printing\",\"proc-macro\"],\"name\":\"syn\",\"req\":\"^1.0.98\"}],\"features\":{}}", "ctor_0.6.3": "{\"dependencies\":[{\"name\":\"ctor-proc-macro\",\"optional\":true,\"req\":\"=0.0.7\"},{\"default_features\":false,\"name\":\"dtor\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"libc-print\",\"req\":\"^0.1.20\"}],\"features\":{\"__no_warn_on_missing_unsafe\":[\"dtor?/__no_warn_on_missing_unsafe\"],\"default\":[\"dtor\",\"proc_macro\",\"__no_warn_on_missing_unsafe\"],\"dtor\":[\"dep:dtor\"],\"proc_macro\":[\"dep:ctor-proc-macro\",\"dtor?/proc_macro\"],\"used_linker\":[\"dtor?/used_linker\"]}}", - "darling_0.20.11": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.20.11\"},{\"name\":\"darling_macro\",\"req\":\"=0.20.11\"},{\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.9\",\"target\":\"cfg(compiletests)\"},{\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0.15\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.89\",\"target\":\"cfg(compiletests)\"}],\"features\":{\"default\":[\"suggestions\"],\"diagnostics\":[\"darling_core/diagnostics\"],\"suggestions\":[\"darling_core/suggestions\"]}}", + "curve25519-dalek-derive_0.1.1": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.66\"},{\"name\":\"quote\",\"req\":\"^1.0.31\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.27\"}],\"features\":{}}", + "curve25519-dalek_4.1.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1\"},{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"cpufeatures\",\"req\":\"^0.2.6\",\"target\":\"cfg(target_arch = \\\"x86_64\\\")\"},{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"curve25519-dalek-derive\",\"req\":\"^0.1\",\"target\":\"cfg(all(not(curve25519_dalek_backend = \\\"fiat\\\"), not(curve25519_dalek_backend = \\\"serial\\\"), target_arch = \\\"x86_64\\\"))\"},{\"default_features\":false,\"name\":\"digest\",\"optional\":true,\"req\":\"^0.10\"},{\"default_features\":false,\"name\":\"ff\",\"optional\":true,\"req\":\"^0.13\"},{\"default_features\":false,\"name\":\"fiat-crypto\",\"req\":\"^0.2.1\",\"target\":\"cfg(curve25519_dalek_backend = \\\"fiat\\\")\"},{\"default_features\":false,\"name\":\"group\",\"optional\":true,\"req\":\"^0.13\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.2\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"rand_core\",\"optional\":true,\"req\":\"^0.6.4\"},{\"default_features\":false,\"features\":[\"getrandom\"],\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"kind\":\"build\",\"name\":\"rustc_version\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2.3.0\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[\"zeroize?/alloc\"],\"default\":[\"alloc\",\"precomputed-tables\",\"zeroize\"],\"group\":[\"dep:group\",\"rand_core\"],\"group-bits\":[\"group\",\"ff/bits\"],\"legacy_compatibility\":[],\"precomputed-tables\":[]}}", "darling_0.21.3": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.21.3\"},{\"name\":\"darling_macro\",\"req\":\"=0.21.3\"},{\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.9\",\"target\":\"cfg(compiletests)\"},{\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0.15\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.89\",\"target\":\"cfg(compiletests)\"}],\"features\":{\"default\":[\"suggestions\"],\"diagnostics\":[\"darling_core/diagnostics\"],\"serde\":[\"darling_core/serde\"],\"suggestions\":[\"darling_core/suggestions\"]}}", "darling_0.23.0": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.23.0\"},{\"name\":\"darling_macro\",\"req\":\"=0.23.0\"},{\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.9\",\"target\":\"cfg(compiletests)\"},{\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0.15\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.89\",\"target\":\"cfg(compiletests)\"}],\"features\":{\"default\":[\"suggestions\"],\"diagnostics\":[\"darling_core/diagnostics\"],\"serde\":[\"darling_core/serde\"],\"suggestions\":[\"darling_core/suggestions\"]}}", - "darling_core_0.20.11": "{\"dependencies\":[{\"name\":\"fnv\",\"req\":\"^1.0.7\"},{\"name\":\"ident_case\",\"req\":\"^1.0.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{\"diagnostics\":[],\"suggestions\":[\"strsim\"]}}", "darling_core_0.21.3": "{\"dependencies\":[{\"name\":\"fnv\",\"req\":\"^1.0.7\"},{\"name\":\"ident_case\",\"req\":\"^1.0.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.210\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.140\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{\"diagnostics\":[],\"suggestions\":[\"strsim\"]}}", "darling_core_0.23.0": "{\"dependencies\":[{\"name\":\"ident_case\",\"req\":\"^1.0.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.210\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.140\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{\"diagnostics\":[],\"suggestions\":[\"strsim\"]}}", - "darling_macro_0.20.11": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.20.11\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{}}", "darling_macro_0.21.3": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.21.3\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{}}", "darling_macro_0.23.0": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.23.0\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{}}", "data-encoding_2.10.0": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "dbus-secret-service_4.1.0": "{\"dependencies\":[{\"name\":\"aes\",\"optional\":true,\"req\":\"^0.8\"},{\"features\":[\"std\"],\"name\":\"block-padding\",\"optional\":true,\"req\":\"^0.3\"},{\"features\":[\"block-padding\",\"alloc\"],\"name\":\"cbc\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"dbus\",\"req\":\"^0.9\"},{\"name\":\"fastrand\",\"optional\":true,\"req\":\"^2.3\"},{\"name\":\"hkdf\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"num\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10.55\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10\"},{\"features\":[\"derive\"],\"name\":\"zeroize\",\"req\":\"^1.8\"}],\"features\":{\"crypto-openssl\":[\"dep:fastrand\",\"dep:num\",\"dep:once_cell\",\"dep:openssl\"],\"crypto-rust\":[\"dep:aes\",\"dep:block-padding\",\"dep:cbc\",\"dep:fastrand\",\"dep:hkdf\",\"dep:num\",\"dep:once_cell\",\"dep:sha2\"],\"vendored\":[\"dbus/vendored\",\"openssl?/vendored\"]}}", - "dbus_0.9.9": "{\"dependencies\":[{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"futures-executor\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"libc\",\"req\":\"^0.2.66\"},{\"name\":\"libdbus-sys\",\"req\":\"^0.2.6\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"},{\"features\":[\"Win32_Networking_WinSock\"],\"name\":\"windows-sys\",\"req\":\"^0.59.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"futures\":[\"futures-util\",\"futures-channel\"],\"no-string-validation\":[],\"stdfd\":[],\"vendored\":[\"libdbus-sys/vendored\"]}}", + "dbus_0.9.10": "{\"dependencies\":[{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"futures-executor\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"libc\",\"req\":\"^0.2.66\"},{\"name\":\"libdbus-sys\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"},{\"features\":[\"Win32_Networking_WinSock\"],\"name\":\"windows-sys\",\"req\":\"^0.59.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"futures\":[\"futures-util\",\"futures-channel\"],\"no-string-validation\":[],\"stdfd\":[],\"vendored\":[\"libdbus-sys/vendored\"]}}", "deadpool-runtime_0.1.4": "{\"dependencies\":[{\"features\":[\"unstable\"],\"name\":\"async-std_1\",\"optional\":true,\"package\":\"async-std\",\"req\":\"^1.0\"},{\"features\":[\"time\",\"rt\"],\"name\":\"tokio_1\",\"optional\":true,\"package\":\"tokio\",\"req\":\"^1.0\"}],\"features\":{}}", "deadpool_0.12.3": "{\"dependencies\":[{\"features\":[\"attributes\"],\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.0\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"config\",\"req\":\"^0.15\"},{\"features\":[\"html_reports\",\"async_tokio\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"deadpool-runtime\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.14\"},{\"name\":\"lazy_static\",\"req\":\"^1.5.0\"},{\"name\":\"num_cpus\",\"req\":\"^1.11.1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.103\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1.5\"},{\"features\":[\"macros\",\"rt\",\"rt-multi-thread\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.5.0\"}],\"features\":{\"default\":[\"managed\",\"unmanaged\"],\"managed\":[],\"rt_async-std_1\":[\"deadpool-runtime/async-std_1\"],\"rt_tokio_1\":[\"deadpool-runtime/tokio_1\"],\"unmanaged\":[]}}", "debugid_0.8.0": "{\"dependencies\":[{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.85\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.37\"},{\"name\":\"uuid\",\"req\":\"^1.0.0\"}],\"features\":{}}", "debugserver-types_0.5.0": "{\"dependencies\":[{\"name\":\"schemafy\",\"req\":\"^0.5.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{}}", + "deflate64_0.1.10": "{\"dependencies\":[{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.13.1\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.7.1\"}],\"features\":{}}", "der_0.7.10": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.3\"},{\"default_features\":false,\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"const-oid\",\"optional\":true,\"req\":\"^0.9.2\"},{\"name\":\"der_derive\",\"optional\":true,\"req\":\"^0.7.2\"},{\"name\":\"flagset\",\"optional\":true,\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4.1\"},{\"features\":[\"alloc\"],\"name\":\"pem-rfc7468\",\"optional\":true,\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.4\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.5\"}],\"features\":{\"alloc\":[\"zeroize?/alloc\"],\"arbitrary\":[\"dep:arbitrary\",\"const-oid?/arbitrary\",\"std\"],\"bytes\":[\"dep:bytes\",\"alloc\"],\"derive\":[\"dep:der_derive\"],\"oid\":[\"dep:const-oid\"],\"pem\":[\"dep:pem-rfc7468\",\"alloc\",\"zeroize\"],\"real\":[],\"std\":[\"alloc\"]}}", - "deranged_0.5.4": "{\"dependencies\":[{\"name\":\"deranged-macros\",\"optional\":true,\"req\":\"=0.3.0\"},{\"default_features\":false,\"name\":\"num-traits\",\"optional\":true,\"req\":\"^0.2.15\"},{\"default_features\":false,\"name\":\"powerfmt\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0.3\"},{\"default_features\":false,\"name\":\"rand08\",\"optional\":true,\"package\":\"rand\",\"req\":\"^0.8.4\"},{\"kind\":\"dev\",\"name\":\"rand08\",\"package\":\"rand\",\"req\":\"^0.8.4\"},{\"default_features\":false,\"name\":\"rand09\",\"optional\":true,\"package\":\"rand\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rand09\",\"package\":\"rand\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.86\"}],\"features\":{\"alloc\":[],\"default\":[],\"macros\":[\"dep:deranged-macros\"],\"num\":[\"dep:num-traits\"],\"powerfmt\":[\"dep:powerfmt\"],\"quickcheck\":[\"dep:quickcheck\",\"alloc\"],\"rand\":[\"rand08\",\"rand09\"],\"rand08\":[\"dep:rand08\"],\"rand09\":[\"dep:rand09\"],\"serde\":[\"dep:serde_core\"]}}", + "deranged_0.5.5": "{\"dependencies\":[{\"name\":\"deranged-macros\",\"optional\":true,\"req\":\"=0.3.0\"},{\"default_features\":false,\"name\":\"num-traits\",\"optional\":true,\"req\":\"^0.2.15\"},{\"default_features\":false,\"name\":\"powerfmt\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0.3\"},{\"default_features\":false,\"name\":\"rand08\",\"optional\":true,\"package\":\"rand\",\"req\":\"^0.8.4\"},{\"kind\":\"dev\",\"name\":\"rand08\",\"package\":\"rand\",\"req\":\"^0.8.4\"},{\"default_features\":false,\"name\":\"rand09\",\"optional\":true,\"package\":\"rand\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rand09\",\"package\":\"rand\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.86\"}],\"features\":{\"alloc\":[],\"default\":[],\"macros\":[\"dep:deranged-macros\"],\"num\":[\"dep:num-traits\"],\"powerfmt\":[\"dep:powerfmt\"],\"quickcheck\":[\"dep:quickcheck\",\"alloc\"],\"rand\":[\"rand08\",\"rand09\"],\"rand08\":[\"dep:rand08\"],\"rand09\":[\"dep:rand09\"],\"serde\":[\"dep:serde_core\"]}}", "derivative_2.2.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"visit\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.18, < 1.0.23\"}],\"features\":{\"use_core\":[]}}", + "derive_arbitrary_1.4.2": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"derive\",\"parsing\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", "derive_more-impl_1.0.0": "{\"dependencies\":[{\"name\":\"convert_case\",\"optional\":true,\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.13.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"rustc_version\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"syn\",\"req\":\"^2.0.45\"},{\"name\":\"unicode-xid\",\"optional\":true,\"req\":\"^0.2.2\"}],\"features\":{\"add\":[],\"add_assign\":[],\"as_ref\":[\"syn/extra-traits\",\"syn/visit\"],\"constructor\":[],\"debug\":[\"syn/extra-traits\",\"dep:unicode-xid\"],\"default\":[],\"deref\":[],\"deref_mut\":[],\"display\":[\"syn/extra-traits\",\"dep:unicode-xid\"],\"error\":[\"syn/extra-traits\"],\"from\":[\"syn/extra-traits\"],\"from_str\":[],\"full\":[\"add\",\"add_assign\",\"as_ref\",\"constructor\",\"debug\",\"deref\",\"deref_mut\",\"display\",\"error\",\"from\",\"from_str\",\"index\",\"index_mut\",\"into\",\"into_iterator\",\"is_variant\",\"mul\",\"mul_assign\",\"not\",\"sum\",\"try_from\",\"try_into\",\"try_unwrap\",\"unwrap\"],\"index\":[],\"index_mut\":[],\"into\":[\"syn/extra-traits\"],\"into_iterator\":[],\"is_variant\":[\"dep:convert_case\"],\"mul\":[\"syn/extra-traits\"],\"mul_assign\":[\"syn/extra-traits\"],\"not\":[\"syn/extra-traits\"],\"sum\":[],\"testing-helpers\":[\"dep:rustc_version\"],\"try_from\":[],\"try_into\":[\"syn/extra-traits\"],\"try_unwrap\":[\"dep:convert_case\"],\"unwrap\":[\"dep:convert_case\"]}}", "derive_more-impl_2.1.1": "{\"dependencies\":[{\"name\":\"convert_case\",\"optional\":true,\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.14.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"rustc_version\",\"req\":\"^0.4\"},{\"name\":\"syn\",\"req\":\"^2.0.45\"},{\"name\":\"unicode-xid\",\"optional\":true,\"req\":\"^0.2.2\"}],\"features\":{\"add\":[\"syn/extra-traits\",\"syn/visit\"],\"add_assign\":[\"syn/extra-traits\",\"syn/visit\"],\"as_ref\":[\"syn/extra-traits\",\"syn/visit\"],\"constructor\":[],\"debug\":[\"syn/extra-traits\",\"dep:unicode-xid\"],\"default\":[],\"deref\":[],\"deref_mut\":[],\"display\":[\"syn/extra-traits\",\"dep:unicode-xid\",\"dep:convert_case\"],\"eq\":[\"syn/extra-traits\",\"syn/visit\"],\"error\":[\"syn/extra-traits\"],\"from\":[\"syn/extra-traits\"],\"from_str\":[\"syn/full\",\"syn/visit\",\"dep:convert_case\"],\"full\":[\"add\",\"add_assign\",\"as_ref\",\"constructor\",\"debug\",\"deref\",\"deref_mut\",\"display\",\"eq\",\"error\",\"from\",\"from_str\",\"index\",\"index_mut\",\"into\",\"into_iterator\",\"is_variant\",\"mul\",\"mul_assign\",\"not\",\"sum\",\"try_from\",\"try_into\",\"try_unwrap\",\"unwrap\"],\"index\":[],\"index_mut\":[],\"into\":[\"syn/extra-traits\",\"syn/visit-mut\"],\"into_iterator\":[],\"is_variant\":[\"dep:convert_case\"],\"mul\":[\"syn/extra-traits\",\"syn/visit\"],\"mul_assign\":[\"syn/extra-traits\",\"syn/visit\"],\"not\":[\"syn/extra-traits\"],\"sum\":[],\"testing-helpers\":[\"syn/full\"],\"try_from\":[],\"try_into\":[\"syn/extra-traits\",\"syn/full\",\"syn/visit-mut\"],\"try_unwrap\":[\"dep:convert_case\"],\"unwrap\":[\"dep:convert_case\"]}}", "derive_more_1.0.0": "{\"dependencies\":[{\"name\":\"derive_more-impl\",\"req\":\"=1.0.0\"},{\"kind\":\"build\",\"name\":\"rustc_version\",\"optional\":true,\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.56\"}],\"features\":{\"add\":[\"derive_more-impl/add\"],\"add_assign\":[\"derive_more-impl/add_assign\"],\"as_ref\":[\"derive_more-impl/as_ref\"],\"constructor\":[\"derive_more-impl/constructor\"],\"debug\":[\"derive_more-impl/debug\"],\"default\":[\"std\"],\"deref\":[\"derive_more-impl/deref\"],\"deref_mut\":[\"derive_more-impl/deref_mut\"],\"display\":[\"derive_more-impl/display\"],\"error\":[\"derive_more-impl/error\"],\"from\":[\"derive_more-impl/from\"],\"from_str\":[\"derive_more-impl/from_str\"],\"full\":[\"add\",\"add_assign\",\"as_ref\",\"constructor\",\"debug\",\"deref\",\"deref_mut\",\"display\",\"error\",\"from\",\"from_str\",\"index\",\"index_mut\",\"into\",\"into_iterator\",\"is_variant\",\"mul\",\"mul_assign\",\"not\",\"sum\",\"try_from\",\"try_into\",\"try_unwrap\",\"unwrap\"],\"index\":[\"derive_more-impl/index\"],\"index_mut\":[\"derive_more-impl/index_mut\"],\"into\":[\"derive_more-impl/into\"],\"into_iterator\":[\"derive_more-impl/into_iterator\"],\"is_variant\":[\"derive_more-impl/is_variant\"],\"mul\":[\"derive_more-impl/mul\"],\"mul_assign\":[\"derive_more-impl/mul_assign\"],\"not\":[\"derive_more-impl/not\"],\"std\":[],\"sum\":[\"derive_more-impl/sum\"],\"testing-helpers\":[\"derive_more-impl/testing-helpers\",\"dep:rustc_version\"],\"try_from\":[\"derive_more-impl/try_from\"],\"try_into\":[\"derive_more-impl/try_into\"],\"try_unwrap\":[\"derive_more-impl/try_unwrap\"],\"unwrap\":[\"derive_more-impl/unwrap\"]}}", @@ -712,36 +727,35 @@ "dispatch2_0.3.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"alloc\":[],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"block2\",\"libc\",\"objc2\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\"],\"std\":[\"alloc\"]}}", "display_container_0.9.0": "{\"dependencies\":[{\"name\":\"either\",\"req\":\"^1.8\"},{\"name\":\"indenter\",\"req\":\"^0.3.3\"}],\"features\":{}}", "displaydoc_0.2.5": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^0.6.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^1.0.24\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", - "doc-comment_0.3.3": "{\"dependencies\":[],\"features\":{\"no_core\":[],\"old_macros\":[]}}", "dotenvy_0.15.7": "{\"dependencies\":[{\"name\":\"clap\",\"optional\":true,\"req\":\"^3.2\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.16.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.3.0\"}],\"features\":{\"cli\":[\"clap\"]}}", "downcast-rs_1.2.1": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "dtor-proc-macro_0.0.6": "{\"dependencies\":[],\"features\":{\"default\":[]}}", - "dtor_0.1.0": "{\"dependencies\":[{\"name\":\"dtor-proc-macro\",\"optional\":true,\"req\":\"=0.0.6\"},{\"kind\":\"dev\",\"name\":\"libc-print\",\"req\":\"^0.1.20\"}],\"features\":{\"__no_warn_on_missing_unsafe\":[],\"default\":[\"proc_macro\",\"__no_warn_on_missing_unsafe\"],\"proc_macro\":[\"dep:dtor-proc-macro\"],\"used_linker\":[]}}", + "dtor_0.1.1": "{\"dependencies\":[{\"name\":\"dtor-proc-macro\",\"optional\":true,\"req\":\"=0.0.6\"},{\"kind\":\"dev\",\"name\":\"libc-print\",\"req\":\"^0.1.20\"}],\"features\":{\"__no_warn_on_missing_unsafe\":[],\"default\":[\"proc_macro\",\"__no_warn_on_missing_unsafe\"],\"proc_macro\":[\"dep:dtor-proc-macro\"],\"used_linker\":[]}}", "dunce_1.0.5": "{\"dependencies\":[],\"features\":{}}", "dupe_0.9.1": "{\"dependencies\":[{\"name\":\"dupe_derive\",\"req\":\"=0.9.1\"}],\"features\":{}}", "dupe_derive_0.9.1": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0.3\"},{\"features\":[\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", - "dyn-clone_1.0.19": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.66\"}],\"features\":{}}", + "dyn-clone_1.0.20": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.66\"}],\"features\":{}}", "either_1.15.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\",\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.95\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[],\"use_std\":[\"std\"]}}", "ena_0.14.3": "{\"dependencies\":[{\"name\":\"dogged\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"log\",\"req\":\"^0.4\"}],\"features\":{\"bench\":[],\"persistent\":[\"dogged\"]}}", "encode_unicode_1.0.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"ascii\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.0\",\"target\":\"cfg(unix)\"},{\"features\":[\"https-native\"],\"kind\":\"dev\",\"name\":\"minreq\",\"req\":\"^2.6\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "encoding_rs_0.8.35": "{\"dependencies\":[{\"name\":\"any_all_workaround\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.0\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"alloc\":[],\"default\":[\"alloc\"],\"fast-big5-hanzi-encode\":[],\"fast-gb-hanzi-encode\":[],\"fast-hangul-encode\":[],\"fast-hanja-encode\":[],\"fast-kanji-encode\":[],\"fast-legacy-encode\":[\"fast-hangul-encode\",\"fast-hanja-encode\",\"fast-kanji-encode\",\"fast-gb-hanzi-encode\",\"fast-big5-hanzi-encode\"],\"less-slow-big5-hanzi-encode\":[],\"less-slow-gb-hanzi-encode\":[],\"less-slow-kanji-encode\":[],\"simd-accel\":[\"any_all_workaround\"]}}", - "endi_1.1.0": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "endi_1.1.1": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "endian-type_0.1.2": "{\"dependencies\":[],\"features\":{}}", "endian-type_0.2.0": "{\"dependencies\":[],\"features\":{}}", "enum-as-inner_0.6.1": "{\"dependencies\":[{\"name\":\"heck\",\"req\":\"^0.5\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", "enumflags2_0.7.12": "{\"dependencies\":[{\"name\":\"enumflags2_derive\",\"req\":\"=0.7.12\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.0\"}],\"features\":{\"std\":[]}}", "enumflags2_derive_0.7.12": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"parsing\",\"printing\",\"derive\",\"proc-macro\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", "env-flags_0.1.1": "{\"dependencies\":[],\"features\":{}}", - "env_filter_0.1.3": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"log\",\"req\":\"^0.4.8\"},{\"default_features\":false,\"features\":[\"std\",\"perf\"],\"name\":\"regex\",\"optional\":true,\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6\"}],\"features\":{\"default\":[\"regex\"],\"regex\":[\"dep:regex\"]}}", + "env_filter_0.1.4": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"log\",\"req\":\"^0.4.8\"},{\"default_features\":false,\"features\":[\"std\",\"perf\"],\"name\":\"regex\",\"optional\":true,\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6\"}],\"features\":{\"default\":[\"regex\"],\"regex\":[\"dep:regex\"]}}", "env_home_0.1.0": "{\"dependencies\":[],\"features\":{}}", "env_logger_0.11.8": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"wincon\"],\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.11\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.6\"},{\"default_features\":false,\"name\":\"env_filter\",\"req\":\"^0.1.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"jiff\",\"optional\":true,\"req\":\"^0.2.3\"},{\"features\":[\"std\"],\"name\":\"log\",\"req\":\"^0.4.21\"}],\"features\":{\"auto-color\":[\"color\",\"anstream/auto\"],\"color\":[\"dep:anstream\",\"dep:anstyle\"],\"default\":[\"auto-color\",\"humantime\",\"regex\"],\"humantime\":[\"dep:jiff\"],\"kv\":[\"log/kv\"],\"regex\":[\"env_filter/regex\"],\"unstable-kv\":[\"kv\"]}}", "equivalent_1.0.2": "{\"dependencies\":[],\"features\":{}}", "erased-serde_0.3.31": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"default_features\":false,\"name\":\"serde\",\"req\":\"^1.0.166\"},{\"kind\":\"dev\",\"name\":\"serde_cbor\",\"req\":\"^0.11.2\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.166\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.99\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.83\"}],\"features\":{\"alloc\":[\"serde/alloc\"],\"default\":[\"std\"],\"std\":[\"serde/std\"],\"unstable-debug\":[]}}", - "errno_0.3.13": "{\"dependencies\":[{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os=\\\"hermit\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os=\\\"wasi\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Diagnostics_Debug\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <=0.60\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"libc/std\"]}}", + "errno_0.3.14": "{\"dependencies\":[{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os=\\\"hermit\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os=\\\"wasi\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Diagnostics_Debug\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"libc/std\"]}}", "error-code_3.3.2": "{\"dependencies\":[],\"features\":{\"std\":[]}}", "etcetera_0.8.0": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"home\",\"req\":\"^0.5\"},{\"features\":[\"Win32_Foundation\",\"Win32_UI_Shell\"],\"name\":\"windows-sys\",\"req\":\"^0.48\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "event-listener-strategy_0.5.4": "{\"dependencies\":[{\"default_features\":false,\"name\":\"event-listener\",\"req\":\"^5.0.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.12\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.37\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"loom\":[\"event-listener/loom\"],\"portable-atomic\":[\"event-listener/portable-atomic\"],\"std\":[\"event-listener/std\"]}}", - "event-listener_5.4.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"concurrent-queue\",\"req\":\"^2.4.0\"},{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"critical-section\",\"optional\":true,\"req\":\"^1.2.0\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"critical-section\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"name\":\"parking\",\"optional\":true,\"req\":\"^2.0.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.12\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"portable-atomic-util\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"portable_atomic_crate\",\"optional\":true,\"package\":\"portable-atomic\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"try-lock\",\"req\":\"^0.2.5\"},{\"kind\":\"dev\",\"name\":\"waker-fn\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"loom\":[\"concurrent-queue/loom\",\"parking?/loom\",\"dep:loom\"],\"portable-atomic\":[\"portable-atomic-util\",\"portable_atomic_crate\",\"concurrent-queue/portable-atomic\"],\"std\":[\"concurrent-queue/std\",\"parking\"]}}", + "event-listener_5.4.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"concurrent-queue\",\"req\":\"^2.4.0\"},{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"critical-section\",\"optional\":true,\"req\":\"^1.2.0\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"critical-section\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"name\":\"parking\",\"optional\":true,\"req\":\"^2.0.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.12\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"portable-atomic-util\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"portable_atomic_crate\",\"optional\":true,\"package\":\"portable-atomic\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"try-lock\",\"req\":\"^0.2.5\"},{\"kind\":\"dev\",\"name\":\"waker-fn\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"loom\":[\"concurrent-queue/loom\",\"parking?/loom\",\"dep:loom\"],\"portable-atomic\":[\"portable-atomic-util\",\"portable_atomic_crate\",\"concurrent-queue/portable-atomic\"],\"std\":[\"concurrent-queue/std\",\"parking\"]}}", "eventsource-stream_0.2.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"http\",\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"nom\",\"req\":\"^7.1\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.8\"},{\"features\":[\"stream\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.11\"},{\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"url\",\"req\":\"^2.2\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"futures-core/std\",\"nom/std\"]}}", "eyre_0.6.12": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.28\"},{\"kind\":\"dev\",\"name\":\"backtrace\",\"req\":\"^0.3.46\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"indenter\",\"req\":\"^0.3.0\"},{\"name\":\"once_cell\",\"req\":\"^1.18.0\"},{\"default_features\":false,\"name\":\"pyo3\",\"optional\":true,\"req\":\"^0.20\"},{\"default_features\":false,\"features\":[\"auto-initialize\"],\"kind\":\"dev\",\"name\":\"pyo3\",\"req\":\"^0.20\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.19\"}],\"features\":{\"auto-install\":[],\"default\":[\"auto-install\",\"track-caller\"],\"track-caller\":[]}}", "fastrand_2.3.0": "{\"dependencies\":[{\"features\":[\"js\"],\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wyhash\",\"req\":\"^0.5\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"js\":[\"std\",\"getrandom\"],\"std\":[\"alloc\"]}}", @@ -749,13 +763,20 @@ "fax_derive_0.2.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", "fd-lock_4.0.4": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"features\":[\"fs\"],\"name\":\"rustix\",\"req\":\"^1.0.0\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.0.8\"},{\"features\":[\"Win32_Foundation\",\"Win32_Storage_FileSystem\",\"Win32_System_IO\"],\"name\":\"windows-sys\",\"req\":\">=0.52.0, <0.60.0\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "fdeflate_0.3.7": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"miniz_oxide\",\"req\":\"^0.7.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"name\":\"simd-adler32\",\"req\":\"^0.3.4\"}],\"features\":{}}", + "fiat-crypto_0.2.9": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "filedescriptor_0.8.3": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"features\":[\"winuser\",\"handleapi\",\"fileapi\",\"namedpipeapi\",\"processthreadsapi\",\"winsock2\",\"processenv\"],\"name\":\"winapi\",\"req\":\"^0.3\",\"target\":\"cfg(windows)\"}],\"features\":{}}", - "find-msvc-tools_0.1.7": "{\"dependencies\":[],\"features\":{}}", + "find-crate_0.6.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"semver\",\"req\":\"^0.11\"},{\"name\":\"toml\",\"req\":\"^0.5.2\"}],\"features\":{}}", + "find-msvc-tools_0.1.9": "{\"dependencies\":[],\"features\":{}}", "findshlibs_0.10.2": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.67\"},{\"name\":\"lazy_static\",\"req\":\"^1.4\",\"target\":\"cfg(any(target_os = \\\"macos\\\", target_os = \\\"ios\\\"))\"},{\"name\":\"libc\",\"req\":\"^0.2.104\"},{\"features\":[\"psapi\",\"memoryapi\",\"libloaderapi\",\"processthreadsapi\"],\"name\":\"winapi\",\"req\":\"^0.3.9\",\"target\":\"cfg(target_os = \\\"windows\\\")\"}],\"features\":{}}", - "fixed_decimal_0.7.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2.3\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand_distr\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"small\"],\"name\":\"ryu\",\"optional\":true,\"req\":\"^1.0.5\"},{\"default_features\":false,\"name\":\"smallvec\",\"req\":\"^1.10.0\"},{\"default_features\":false,\"name\":\"writeable\",\"req\":\"^0.6.0\"}],\"features\":{\"experimental\":[],\"ryu\":[\"dep:ryu\"]}}", + "fixed_decimal_0.7.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2.3\"},{\"features\":[\"wasm_js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_distr\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"small\"],\"name\":\"ryu\",\"optional\":true,\"req\":\"^1.0.5\"},{\"default_features\":false,\"name\":\"smallvec\",\"req\":\"^1.10.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"writeable\",\"req\":\"^0.6.0\"}],\"features\":{\"experimental\":[],\"ryu\":[\"dep:ryu\"]}}", "fixedbitset_0.4.2": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", - "flate2_1.1.2": "{\"dependencies\":[{\"name\":\"cloudflare-zlib-sys\",\"optional\":true,\"req\":\"^0.3.5\"},{\"name\":\"crc32fast\",\"req\":\"^1.2.0\"},{\"name\":\"libz-ng-sys\",\"optional\":true,\"req\":\"^1.1.16\"},{\"default_features\":false,\"features\":[\"std\",\"rust-allocator\"],\"name\":\"libz-rs-sys\",\"optional\":true,\"req\":\"^0.5.1\"},{\"default_features\":false,\"name\":\"libz-sys\",\"optional\":true,\"req\":\"^1.1.20\"},{\"default_features\":false,\"features\":[\"with-alloc\"],\"name\":\"miniz_oxide\",\"req\":\"^0.8.5\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"emscripten\\\")))\"},{\"default_features\":false,\"features\":[\"with-alloc\"],\"name\":\"miniz_oxide\",\"optional\":true,\"req\":\"^0.8.5\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"}],\"features\":{\"any_impl\":[],\"any_zlib\":[\"any_impl\"],\"cloudflare_zlib\":[\"any_zlib\",\"cloudflare-zlib-sys\"],\"default\":[\"rust_backend\"],\"miniz-sys\":[\"rust_backend\"],\"rust_backend\":[\"miniz_oxide\",\"any_impl\"],\"zlib\":[\"any_zlib\",\"libz-sys\"],\"zlib-default\":[\"any_zlib\",\"libz-sys/default\"],\"zlib-ng\":[\"any_zlib\",\"libz-ng-sys\"],\"zlib-ng-compat\":[\"zlib\",\"libz-sys/zlib-ng\"],\"zlib-rs\":[\"any_zlib\",\"libz-rs-sys\"]}}", + "fixedbitset_0.5.7": "{\"dependencies\":[{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "flate2_1.1.8": "{\"dependencies\":[{\"name\":\"cloudflare-zlib-sys\",\"optional\":true,\"req\":\"^0.3.6\"},{\"name\":\"crc32fast\",\"req\":\"^1.2.0\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"libz-ng-sys\",\"optional\":true,\"req\":\"^1.1.16\"},{\"default_features\":false,\"name\":\"libz-sys\",\"optional\":true,\"req\":\"^1.1.20\"},{\"default_features\":false,\"features\":[\"with-alloc\",\"simd\"],\"name\":\"miniz_oxide\",\"req\":\"^0.8.5\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"emscripten\\\")))\"},{\"default_features\":false,\"features\":[\"with-alloc\",\"simd\"],\"name\":\"miniz_oxide\",\"optional\":true,\"req\":\"^0.8.5\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"std\",\"rust-allocator\"],\"name\":\"zlib-rs\",\"optional\":true,\"req\":\"^0.5.5\"}],\"features\":{\"any_c_zlib\":[\"any_zlib\"],\"any_impl\":[],\"any_zlib\":[\"any_impl\"],\"cloudflare_zlib\":[\"any_c_zlib\",\"cloudflare-zlib-sys\"],\"default\":[\"rust_backend\"],\"miniz-sys\":[\"rust_backend\"],\"rust_backend\":[\"miniz_oxide\",\"any_impl\"],\"zlib\":[\"any_c_zlib\",\"libz-sys\"],\"zlib-default\":[\"any_c_zlib\",\"libz-sys/default\"],\"zlib-ng\":[\"any_c_zlib\",\"libz-ng-sys\"],\"zlib-ng-compat\":[\"zlib\",\"libz-sys/zlib-ng\"],\"zlib-rs\":[\"any_zlib\",\"dep:zlib-rs\"]}}", "float-cmp_0.10.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"num-traits\",\"optional\":true,\"req\":\"^0.2.1\"}],\"features\":{\"default\":[\"ratio\"],\"ratio\":[\"num-traits\"],\"std\":[]}}", + "fluent-bundle_0.15.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"name\":\"fluent-langneg\",\"req\":\"^0.13\"},{\"name\":\"fluent-syntax\",\"req\":\"^0.11.1\"},{\"kind\":\"dev\",\"name\":\"iai\",\"req\":\"^0.1\"},{\"name\":\"intl-memoizer\",\"req\":\"^0.5.2\"},{\"name\":\"intl_pluralrules\",\"req\":\"^7.0.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"rustc-hash\",\"req\":\"^1\"},{\"name\":\"self_cell\",\"req\":\"^0.10\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_yaml\",\"req\":\"^0.8\"},{\"name\":\"smallvec\",\"req\":\"^1\"},{\"name\":\"unic-langid\",\"req\":\"^0.9\"},{\"features\":[\"macros\"],\"kind\":\"dev\",\"name\":\"unic-langid\",\"req\":\"^0.9\"}],\"features\":{\"all-benchmarks\":[],\"default\":[]}}", + "fluent-langneg_0.13.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"unic-langid\",\"req\":\"^0.9\"},{\"features\":[\"macros\"],\"kind\":\"dev\",\"name\":\"unic-langid\",\"req\":\"^0.9\"},{\"features\":[\"macros\"],\"kind\":\"dev\",\"name\":\"unic-locale\",\"req\":\"^0.9\"}],\"features\":{\"cldr\":[\"unic-langid/likelysubtags\"],\"default\":[]}}", + "fluent-syntax_0.11.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"iai\",\"req\":\"^0.1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"thiserror\",\"req\":\"^1.0\"}],\"features\":{\"all-benchmarks\":[],\"default\":[],\"json\":[\"serde\",\"serde_json\"]}}", + "fluent_0.16.1": "{\"dependencies\":[{\"name\":\"fluent-bundle\",\"req\":\"^0.15.3\"},{\"name\":\"fluent-pseudo\",\"optional\":true,\"req\":\"^0.3.2\"},{\"name\":\"unic-langid\",\"req\":\"^0.9\"}],\"features\":{}}", "flume_0.11.1": "{\"dependencies\":[{\"features\":[\"attributes\",\"unstable\"],\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.13.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"kind\":\"dev\",\"name\":\"crossbeam-channel\",\"req\":\"^0.5.5\"},{\"kind\":\"dev\",\"name\":\"crossbeam-utils\",\"req\":\"^0.8.10\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-sink\",\"optional\":true,\"req\":\"^0.3\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2.15\"},{\"features\":[\"getrandom\"],\"name\":\"nanorand\",\"optional\":true,\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.3\"},{\"features\":[\"mutex\"],\"name\":\"spin1\",\"package\":\"spin\",\"req\":\"^0.9.8\"},{\"features\":[\"rt\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.16.1\"},{\"kind\":\"dev\",\"name\":\"waker-fn\",\"req\":\"^1.1.0\"}],\"features\":{\"async\":[\"futures-sink\",\"futures-core\"],\"default\":[\"async\",\"select\",\"eventual-fairness\"],\"eventual-fairness\":[\"select\",\"nanorand\"],\"select\":[],\"spin\":[]}}", "flume_0.12.0": "{\"dependencies\":[{\"features\":[\"attributes\",\"unstable\"],\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.13.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"kind\":\"dev\",\"name\":\"crossbeam-channel\",\"req\":\"^0.5.5\"},{\"kind\":\"dev\",\"name\":\"crossbeam-utils\",\"req\":\"^0.8.10\"},{\"features\":[\"std\",\"js\"],\"name\":\"fastrand\",\"optional\":true,\"req\":\"^2.3\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-sink\",\"optional\":true,\"req\":\"^0.3\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2.15\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.3\"},{\"features\":[\"mutex\"],\"name\":\"spin1\",\"package\":\"spin\",\"req\":\"^0.9.8\"},{\"features\":[\"rt\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.16.1\"},{\"kind\":\"dev\",\"name\":\"waker-fn\",\"req\":\"^1.1.0\"}],\"features\":{\"async\":[\"futures-sink\",\"futures-core\"],\"default\":[\"async\",\"select\",\"eventual-fairness\"],\"eventual-fairness\":[\"select\",\"fastrand\"],\"select\":[],\"spin\":[]}}", "fnv_1.0.7": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", @@ -766,7 +787,7 @@ "foreign-types-shared_0.3.1": "{\"dependencies\":[],\"features\":{}}", "foreign-types_0.3.2": "{\"dependencies\":[{\"name\":\"foreign-types-shared\",\"req\":\"^0.1\"}],\"features\":{}}", "foreign-types_0.5.0": "{\"dependencies\":[{\"name\":\"foreign-types-macros\",\"req\":\"^0.2\"},{\"name\":\"foreign-types-shared\",\"req\":\"^0.3\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"foreign-types-macros/std\"]}}", - "form_urlencoded_1.2.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"percent-encoding\",\"req\":\"^2.3.0\"}],\"features\":{\"alloc\":[\"percent-encoding/alloc\"],\"default\":[\"std\"],\"std\":[\"alloc\",\"percent-encoding/std\"]}}", + "form_urlencoded_1.2.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"percent-encoding\",\"req\":\"^2.3.0\"}],\"features\":{\"alloc\":[\"percent-encoding/alloc\"],\"default\":[\"std\"],\"std\":[\"alloc\",\"percent-encoding/std\"]}}", "fs_extra_1.3.0": "{\"dependencies\":[],\"features\":{}}", "fsevent-sys_4.1.0": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.68\"}],\"features\":{}}", "fslock_0.2.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.66\",\"target\":\"cfg(unix)\"},{\"features\":[\"minwindef\",\"minwinbase\",\"winbase\",\"errhandlingapi\",\"winerror\",\"winnt\",\"synchapi\",\"handleapi\",\"fileapi\",\"processthreadsapi\"],\"name\":\"winapi\",\"req\":\"^0.3.8\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", @@ -784,11 +805,11 @@ "fxhash_0.2.1": "{\"dependencies\":[{\"name\":\"byteorder\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.5\"},{\"kind\":\"dev\",\"name\":\"seahash\",\"req\":\"^3.0.5\"}],\"features\":{}}", "generator_0.8.8": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"libc\",\"req\":\"^0.2.100\",\"target\":\"cfg(unix)\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"kind\":\"build\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"name\":\"windows-link\",\"req\":\">=0.1, <=0.2\",\"target\":\"cfg(windows)\"},{\"name\":\"windows-result\",\"req\":\">=0.3.1, <=0.4\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "generic-array_0.14.7": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"typenum\",\"req\":\"^1.12\"},{\"kind\":\"build\",\"name\":\"version_check\",\"req\":\"^0.9\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"more_lengths\":[]}}", - "gethostname_0.4.3": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.141\",\"target\":\"cfg(not(windows))\"},{\"name\":\"windows-targets\",\"req\":\"^0.48\",\"target\":\"cfg(windows)\"}],\"features\":{}}", - "getopts_0.2.23": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"std\",\"optional\":true,\"package\":\"rustc-std-workspace-std\",\"req\":\"^1.0\"},{\"name\":\"unicode-width\",\"req\":\"^0.2.0\"}],\"features\":{\"rustc-dep-of-std\":[\"unicode-width/rustc-dep-of-std\",\"std\",\"core\"]}}", - "getrandom_0.2.16": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(unix)\"},{\"default_features\":false,\"name\":\"wasi\",\"req\":\"^0.11\",\"target\":\"cfg(target_os = \\\"wasi\\\")\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.62\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.18\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"}],\"features\":{\"custom\":[],\"js\":[\"wasm-bindgen\",\"js-sys\"],\"linux_disable_fallback\":[],\"rdrand\":[],\"rustc-dep-of-std\":[\"compiler_builtins\",\"core\",\"libc/rustc-dep-of-std\",\"wasi/rustc-dep-of-std\"],\"std\":[],\"test-in-browser\":[]}}", - "getrandom_0.3.3": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.77\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"), target_feature = \\\"atomics\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(all(any(target_os = \\\"linux\\\", target_os = \\\"android\\\"), not(any(all(target_os = \\\"linux\\\", target_env = \\\"\\\"), getrandom_backend = \\\"custom\\\", getrandom_backend = \\\"linux_raw\\\", getrandom_backend = \\\"rdrand\\\", getrandom_backend = \\\"rndr\\\"))))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"hurd\\\", target_os = \\\"illumos\\\", target_os = \\\"cygwin\\\", all(target_os = \\\"horizon\\\", target_arch = \\\"arm\\\")))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"haiku\\\", target_os = \\\"redox\\\", target_os = \\\"nto\\\", target_os = \\\"aix\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"visionos\\\", target_os = \\\"watchos\\\", target_os = \\\"tvos\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"macos\\\", target_os = \\\"openbsd\\\", target_os = \\\"vita\\\", target_os = \\\"emscripten\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"netbsd\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"solaris\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"vxworks\\\")\"},{\"default_features\":false,\"name\":\"r-efi\",\"req\":\"^5.1\",\"target\":\"cfg(all(target_os = \\\"uefi\\\", getrandom_backend = \\\"efi_rng\\\"))\"},{\"default_features\":false,\"name\":\"wasi\",\"req\":\"^0.14\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"wasi\\\", target_env = \\\"p2\\\"))\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.98\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"}],\"features\":{\"rustc-dep-of-std\":[\"dep:compiler_builtins\",\"dep:core\"],\"std\":[],\"wasm_js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"]}}", - "gimli_0.31.1": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.2\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"fallible-iterator\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.0.0\"},{\"default_features\":false,\"name\":\"stable_deref_trait\",\"optional\":true,\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"test-assembler\",\"req\":\"^0.1.3\"}],\"features\":{\"default\":[\"read-all\",\"write\"],\"endian-reader\":[\"read\",\"dep:stable_deref_trait\"],\"fallible-iterator\":[\"dep:fallible-iterator\"],\"read\":[\"read-core\"],\"read-all\":[\"read\",\"std\",\"fallible-iterator\",\"endian-reader\"],\"read-core\":[],\"rustc-dep-of-std\":[\"dep:core\",\"dep:alloc\",\"dep:compiler_builtins\"],\"std\":[\"fallible-iterator?/std\",\"stable_deref_trait?/std\"],\"write\":[\"dep:indexmap\"]}}", + "gethostname_1.1.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"system\"],\"name\":\"rustix\",\"req\":\"^1.0.3\",\"target\":\"cfg(not(windows))\"},{\"name\":\"windows-link\",\"req\":\"^0.2.1\",\"target\":\"cfg(windows)\"}],\"features\":{}}", + "getopts_0.2.24": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"std\",\"optional\":true,\"package\":\"rustc-std-workspace-std\",\"req\":\"^1.0\"},{\"name\":\"unicode-width\",\"optional\":true,\"req\":\"^0.2.0\"}],\"features\":{\"default\":[\"unicode\"],\"rustc-dep-of-std\":[\"std\",\"core\"],\"unicode\":[\"dep:unicode-width\"]}}", + "getrandom_0.2.17": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(unix)\"},{\"default_features\":false,\"name\":\"wasi\",\"req\":\"^0.11\",\"target\":\"cfg(target_os = \\\"wasi\\\")\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.62\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.18\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"}],\"features\":{\"custom\":[],\"js\":[\"wasm-bindgen\",\"js-sys\"],\"linux_disable_fallback\":[],\"rdrand\":[],\"rustc-dep-of-std\":[\"compiler_builtins\",\"core\",\"libc/rustc-dep-of-std\",\"wasi/rustc-dep-of-std\"],\"std\":[],\"test-in-browser\":[]}}", + "getrandom_0.3.4": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.77\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"), target_feature = \\\"atomics\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(all(any(target_os = \\\"linux\\\", target_os = \\\"android\\\"), not(any(all(target_os = \\\"linux\\\", target_env = \\\"\\\"), getrandom_backend = \\\"custom\\\", getrandom_backend = \\\"linux_raw\\\", getrandom_backend = \\\"rdrand\\\", getrandom_backend = \\\"rndr\\\"))))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"hurd\\\", target_os = \\\"illumos\\\", target_os = \\\"cygwin\\\", all(target_os = \\\"horizon\\\", target_arch = \\\"arm\\\")))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"haiku\\\", target_os = \\\"redox\\\", target_os = \\\"nto\\\", target_os = \\\"aix\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"visionos\\\", target_os = \\\"watchos\\\", target_os = \\\"tvos\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(any(target_os = \\\"macos\\\", target_os = \\\"openbsd\\\", target_os = \\\"vita\\\", target_os = \\\"emscripten\\\"))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"netbsd\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"solaris\\\")\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.154\",\"target\":\"cfg(target_os = \\\"vxworks\\\")\"},{\"default_features\":false,\"name\":\"r-efi\",\"req\":\"^5.1\",\"target\":\"cfg(all(target_os = \\\"uefi\\\", getrandom_backend = \\\"efi_rng\\\"))\"},{\"default_features\":false,\"name\":\"wasip2\",\"req\":\"^1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"wasi\\\", target_env = \\\"p2\\\"))\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.98\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"}],\"features\":{\"std\":[],\"wasm_js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"]}}", + "gimli_0.32.3": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"fallible-iterator\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.0.0\"},{\"default_features\":false,\"name\":\"stable_deref_trait\",\"optional\":true,\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"test-assembler\",\"req\":\"^0.1.3\"}],\"features\":{\"default\":[\"read-all\",\"write\"],\"endian-reader\":[\"read\",\"dep:stable_deref_trait\"],\"fallible-iterator\":[\"dep:fallible-iterator\"],\"read\":[\"read-core\"],\"read-all\":[\"read\",\"std\",\"fallible-iterator\",\"endian-reader\"],\"read-core\":[],\"rustc-dep-of-std\":[\"dep:core\",\"dep:alloc\"],\"std\":[\"fallible-iterator?/std\",\"stable_deref_trait?/std\"],\"write\":[\"dep:indexmap\"]}}", "git+https://github.com/JakkuSakura/tokio-tungstenite?rev=2ae536b0de793f3ddf31fc2f22d445bf1ef2023d#2ae536b0de793f3ddf31fc2f22d445bf1ef2023d_tokio-tungstenite": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"sink\",\"std\"],\"name\":\"futures-util\",\"optional\":false},{\"name\":\"log\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\"},{\"default_features\":false,\"features\":[],\"name\":\"rustls\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"tokio-native-tls\",\"optional\":true},{\"default_features\":false,\"features\":[],\"name\":\"tokio-rustls\",\"optional\":true},{\"default_features\":false,\"features\":[],\"name\":\"tungstenite\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true}],\"features\":{\"__rustls-tls\":[\"rustls\",\"rustls-pki-types\",\"tokio-rustls\",\"stream\",\"tungstenite/__rustls-tls\",\"handshake\"],\"connect\":[\"stream\",\"tokio/net\",\"handshake\"],\"default\":[\"connect\",\"handshake\"],\"handshake\":[\"tungstenite/handshake\"],\"native-tls\":[\"native-tls-crate\",\"tokio-native-tls\",\"stream\",\"tungstenite/native-tls\",\"handshake\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate/vendored\",\"tungstenite/native-tls-vendored\"],\"proxy\":[\"tungstenite/proxy\",\"tokio/net\",\"handshake\"],\"rustls-tls-native-roots\":[\"__rustls-tls\",\"rustls-native-certs\"],\"rustls-tls-webpki-roots\":[\"__rustls-tls\",\"webpki-roots\"],\"stream\":[],\"url\":[\"tungstenite/url\"]},\"strip_prefix\":\"\"}", "git+https://github.com/JakkuSakura/tungstenite-rs?rev=f514de8644821113e5d18a027d6d28a5c8cc0a6e#f514de8644821113e5d18a027d6d28a5c8cc0a6e_tungstenite": "{\"dependencies\":[{\"name\":\"bytes\"},{\"default_features\":true,\"features\":[],\"name\":\"data-encoding\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"http\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"httparse\",\"optional\":true},{\"name\":\"log\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\"},{\"name\":\"rand\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"sha1\",\"optional\":true},{\"name\":\"thiserror\"},{\"default_features\":true,\"features\":[],\"name\":\"url\",\"optional\":true},{\"name\":\"utf-8\"},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true}],\"features\":{\"__rustls-tls\":[\"rustls\",\"rustls-pki-types\"],\"default\":[\"handshake\"],\"handshake\":[\"data-encoding\",\"http\",\"httparse\",\"sha1\"],\"native-tls\":[\"native-tls-crate\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate/vendored\"],\"proxy\":[\"handshake\"],\"rustls-tls-native-roots\":[\"__rustls-tls\",\"rustls-native-certs\"],\"rustls-tls-webpki-roots\":[\"__rustls-tls\",\"webpki-roots\"],\"url\":[\"dep:url\"]},\"strip_prefix\":\"\"}", "git+https://github.com/dzbarsky/rules_rust?rev=b56cbaa8465e74127f1ea216f813cd377295ad81#b56cbaa8465e74127f1ea216f813cd377295ad81_runfiles": "{\"dependencies\":[],\"features\":{},\"strip_prefix\":\"\"}", @@ -798,12 +819,12 @@ "git+https://github.com/nornagon/ratatui?branch=nornagon-v0.29.0-patch#9b2ad1298408c45918ee9f8241a6f95498cdbed2_ratatui": "{\"dependencies\":[{\"name\":\"bitflags\"},{\"name\":\"cassowary\"},{\"name\":\"compact_str\"},{\"default_features\":true,\"features\":[],\"name\":\"crossterm\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"document-features\",\"optional\":true},{\"name\":\"indoc\"},{\"name\":\"instability\"},{\"name\":\"itertools\"},{\"name\":\"lru\"},{\"default_features\":true,\"features\":[],\"name\":\"palette\",\"optional\":true},{\"name\":\"paste\"},{\"default_features\":true,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true},{\"default_features\":true,\"features\":[\"derive\"],\"name\":\"strum\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"termwiz\",\"optional\":true},{\"default_features\":true,\"features\":[\"local-offset\"],\"name\":\"time\",\"optional\":true},{\"name\":\"unicode-segmentation\"},{\"name\":\"unicode-truncate\"},{\"name\":\"unicode-width\"},{\"default_features\":true,\"features\":[],\"name\":\"termion\",\"optional\":true,\"target\":\"cfg(not(windows))\"}],\"features\":{\"all-widgets\":[\"widget-calendar\"],\"crossterm\":[\"dep:crossterm\"],\"default\":[\"crossterm\",\"underline-color\"],\"macros\":[],\"palette\":[\"dep:palette\"],\"scrolling-regions\":[],\"serde\":[\"dep:serde\",\"bitflags/serde\",\"compact_str/serde\"],\"termion\":[\"dep:termion\"],\"termwiz\":[\"dep:termwiz\"],\"underline-color\":[\"dep:crossterm\"],\"unstable\":[\"unstable-rendered-line-info\",\"unstable-widget-ref\",\"unstable-backend-writer\"],\"unstable-backend-writer\":[],\"unstable-rendered-line-info\":[],\"unstable-widget-ref\":[],\"widget-calendar\":[\"dep:time\"]},\"strip_prefix\":\"\"}", "glob_0.3.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"tempdir\",\"req\":\"^0.3\"}],\"features\":{}}", "globset_0.4.18": "{\"dependencies\":[{\"name\":\"aho-corasick\",\"req\":\"^1.1.1\"},{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.3.2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bstr\",\"req\":\"^1.6.2\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3.1\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.20\"},{\"default_features\":false,\"features\":[\"std\",\"perf\",\"syntax\",\"meta\",\"nfa\",\"hybrid\"],\"name\":\"regex-automata\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"regex-syntax\",\"req\":\"^0.8.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.188\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.107\"}],\"features\":{\"arbitrary\":[\"dep:arbitrary\"],\"default\":[\"log\"],\"serde1\":[\"serde\"],\"simd-accel\":[]}}", - "h2_0.4.11": "{\"dependencies\":[{\"name\":\"atomic-waker\",\"req\":\"^1.0.0\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\"},{\"name\":\"fnv\",\"req\":\"^1.0.5\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-sink\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"http\",\"req\":\"^1\"},{\"features\":[\"std\"],\"name\":\"indexmap\",\"req\":\"^2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.4\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.0\"},{\"name\":\"slab\",\"req\":\"^0.4.2\"},{\"features\":[\"io-util\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"rt-multi-thread\",\"macros\",\"sync\",\"net\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-rustls\",\"req\":\"^0.26\"},{\"features\":[\"codec\",\"io\"],\"name\":\"tokio-util\",\"req\":\"^0.7.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.3.2\"},{\"kind\":\"dev\",\"name\":\"webpki-roots\",\"req\":\"^0.26\"}],\"features\":{\"stream\":[],\"unstable\":[]}}", - "half_2.6.0": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.4.1\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"bytemuck\",\"optional\":true,\"req\":\"^1.4.1\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"crunchy\",\"req\":\"^0.2.2\",\"target\":\"cfg(target_arch = \\\"spirv\\\")\"},{\"kind\":\"dev\",\"name\":\"crunchy\",\"req\":\"^0.2.2\"},{\"default_features\":false,\"features\":[\"libm\"],\"name\":\"num-traits\",\"optional\":true,\"req\":\"^0.2.16\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"thread_rng\"],\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"name\":\"rand_distr\",\"optional\":true,\"req\":\"^0.5.0\"},{\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.8.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"zerocopy\",\"optional\":true,\"req\":\"^0.8.23\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"rand_distr\":[\"dep:rand\",\"dep:rand_distr\"],\"std\":[\"alloc\"],\"use-intrinsics\":[]}}", + "h2_0.4.13": "{\"dependencies\":[{\"name\":\"atomic-waker\",\"req\":\"^1.0.0\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\"},{\"name\":\"fnv\",\"req\":\"^1.0.5\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-sink\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"http\",\"req\":\"^1\"},{\"features\":[\"std\"],\"name\":\"indexmap\",\"req\":\"^2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.4\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.0\"},{\"name\":\"slab\",\"req\":\"^0.4.2\"},{\"features\":[\"io-util\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"rt-multi-thread\",\"macros\",\"sync\",\"net\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-rustls\",\"req\":\"^0.26\"},{\"features\":[\"codec\",\"io\"],\"name\":\"tokio-util\",\"req\":\"^0.7.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.3.2\"},{\"kind\":\"dev\",\"name\":\"webpki-roots\",\"req\":\"^1\"}],\"features\":{\"stream\":[],\"unstable\":[]}}", + "half_2.7.1": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.4.1\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"bytemuck\",\"optional\":true,\"req\":\"^1.4.1\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"crunchy\",\"req\":\"^0.2.2\",\"target\":\"cfg(target_arch = \\\"spirv\\\")\"},{\"kind\":\"dev\",\"name\":\"crunchy\",\"req\":\"^0.2.2\"},{\"default_features\":false,\"features\":[\"libm\"],\"name\":\"num-traits\",\"optional\":true,\"req\":\"^0.2.16\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"thread_rng\"],\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"name\":\"rand_distr\",\"optional\":true,\"req\":\"^0.5.0\"},{\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.8.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"derive\",\"simd\"],\"name\":\"zerocopy\",\"req\":\"^0.8.26\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"nightly\":[],\"rand_distr\":[\"dep:rand\",\"dep:rand_distr\"],\"std\":[\"alloc\"],\"use-intrinsics\":[],\"zerocopy\":[]}}", "hashbrown_0.12.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"ahash\",\"optional\":true,\"req\":\"^0.7.0\"},{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"name\":\"bumpalo\",\"optional\":true,\"req\":\"^3.5.0\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.2\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.7\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.4\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.3\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.25\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{\"ahash-compile-time-rng\":[\"ahash/compile-time-rng\"],\"default\":[\"ahash\",\"inline-more\"],\"inline-more\":[],\"nightly\":[],\"raw\":[],\"rustc-dep-of-std\":[\"nightly\",\"core\",\"compiler_builtins\",\"alloc\",\"rustc-internal-api\"],\"rustc-internal-api\":[]}}", "hashbrown_0.14.5": "{\"dependencies\":[{\"default_features\":false,\"name\":\"ahash\",\"optional\":true,\"req\":\"^0.8.7\"},{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"allocator-api2\",\"optional\":true,\"req\":\"^0.2.9\"},{\"features\":[\"allocator-api2\"],\"kind\":\"dev\",\"name\":\"bumpalo\",\"req\":\"^3.13.0\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.2\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"name\":\"equivalent\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.7\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.4\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.3\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.7.42\"},{\"features\":[\"validation\"],\"kind\":\"dev\",\"name\":\"rkyv\",\"req\":\"^0.7.42\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.25\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"ahash\",\"inline-more\",\"allocator-api2\"],\"inline-more\":[],\"nightly\":[\"allocator-api2?/nightly\",\"bumpalo/allocator_api\"],\"raw\":[],\"rustc-dep-of-std\":[\"nightly\",\"core\",\"compiler_builtins\",\"alloc\",\"rustc-internal-api\"],\"rustc-internal-api\":[]}}", - "hashbrown_0.15.4": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"allocator-api2\",\"optional\":true,\"req\":\"^0.2.9\"},{\"features\":[\"allocator-api2\"],\"kind\":\"dev\",\"name\":\"bumpalo\",\"req\":\"^3.13.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"name\":\"equivalent\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.7\"},{\"default_features\":false,\"name\":\"foldhash\",\"optional\":true,\"req\":\"^0.1.2\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.4\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9.0\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.2\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.25\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"default-hasher\",\"inline-more\",\"allocator-api2\",\"equivalent\",\"raw-entry\"],\"default-hasher\":[\"dep:foldhash\"],\"inline-more\":[],\"nightly\":[\"bumpalo/allocator_api\"],\"raw-entry\":[],\"rustc-dep-of-std\":[\"nightly\",\"core\",\"alloc\",\"rustc-internal-api\"],\"rustc-internal-api\":[]}}", - "hashbrown_0.16.0": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"allocator-api2\",\"optional\":true,\"req\":\"^0.2.9\"},{\"features\":[\"allocator-api2\"],\"kind\":\"dev\",\"name\":\"bumpalo\",\"req\":\"^3.13.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"name\":\"equivalent\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.7\"},{\"default_features\":false,\"name\":\"foldhash\",\"optional\":true,\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.4\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9.0\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.2\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.25\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"default-hasher\",\"inline-more\",\"allocator-api2\",\"equivalent\",\"raw-entry\"],\"default-hasher\":[\"dep:foldhash\"],\"inline-more\":[],\"nightly\":[\"foldhash?/nightly\",\"bumpalo/allocator_api\"],\"raw-entry\":[],\"rustc-dep-of-std\":[\"nightly\",\"core\",\"alloc\",\"rustc-internal-api\"],\"rustc-internal-api\":[]}}", + "hashbrown_0.15.5": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"allocator-api2\",\"optional\":true,\"req\":\"^0.2.9\"},{\"features\":[\"allocator-api2\"],\"kind\":\"dev\",\"name\":\"bumpalo\",\"req\":\"^3.13.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"name\":\"equivalent\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.7\"},{\"default_features\":false,\"name\":\"foldhash\",\"optional\":true,\"req\":\"^0.1.2\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.4\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9.0\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.2\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.25\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"default-hasher\",\"inline-more\",\"allocator-api2\",\"equivalent\",\"raw-entry\"],\"default-hasher\":[\"dep:foldhash\"],\"inline-more\":[],\"nightly\":[\"bumpalo/allocator_api\"],\"raw-entry\":[],\"rustc-dep-of-std\":[\"nightly\",\"core\",\"alloc\",\"rustc-internal-api\"],\"rustc-internal-api\":[]}}", + "hashbrown_0.16.1": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"allocator-api2\",\"optional\":true,\"req\":\"^0.2.9\"},{\"features\":[\"allocator-api2\"],\"kind\":\"dev\",\"name\":\"bumpalo\",\"req\":\"^3.13.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"equivalent\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.7\"},{\"default_features\":false,\"name\":\"foldhash\",\"optional\":true,\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.4\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"cfg(unix)\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9.0\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.9.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.2\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.221\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"default-hasher\",\"inline-more\",\"allocator-api2\",\"equivalent\",\"raw-entry\"],\"default-hasher\":[\"dep:foldhash\"],\"inline-more\":[],\"nightly\":[\"foldhash?/nightly\",\"bumpalo/allocator_api\"],\"raw-entry\":[],\"rustc-dep-of-std\":[\"nightly\",\"core\",\"alloc\",\"rustc-internal-api\"],\"rustc-internal-api\":[],\"serde\":[\"dep:serde_core\",\"dep:serde\"]}}", "hashlink_0.10.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"default-hasher\",\"inline-more\"],\"name\":\"hashbrown\",\"req\":\"^0.15\"},{\"kind\":\"dev\",\"name\":\"rustc-hash\",\"req\":\"^2\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{\"serde_impl\":[\"serde\"]}}", "heck_0.5.0": "{\"dependencies\":[],\"features\":{}}", "hermit-abi_0.5.2": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[],\"rustc-dep-of-std\":[\"core\",\"alloc\"]}}", @@ -812,67 +833,74 @@ "hickory-resolver_0.25.2": "{\"dependencies\":[{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.50\"},{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"futures-executor\",\"req\":\"^0.3.5\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-util\",\"req\":\"^0.3.5\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"hickory-proto\",\"req\":\"^0.25\"},{\"name\":\"ipconfig\",\"optional\":true,\"req\":\"^0.3.0\",\"target\":\"cfg(windows)\"},{\"features\":[\"sync\"],\"name\":\"moka\",\"req\":\"^0.12\"},{\"default_features\":false,\"features\":[\"critical-section\"],\"name\":\"once_cell\",\"req\":\"^1.20.0\"},{\"name\":\"parking_lot\",\"req\":\"^0.12\"},{\"default_features\":false,\"features\":[\"log\",\"runtime-tokio\"],\"name\":\"quinn\",\"optional\":true,\"req\":\"^0.11.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"rand\",\"req\":\"^0.9\"},{\"features\":[\"system\"],\"name\":\"resolv-conf\",\"optional\":true,\"req\":\"^0.7.0\"},{\"default_features\":false,\"features\":[\"logging\",\"std\",\"tls12\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.23\"},{\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"name\":\"smallvec\",\"req\":\"^1.6\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2\"},{\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.21\"},{\"features\":[\"macros\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.21\"},{\"default_features\":false,\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26\"},{\"kind\":\"dev\",\"name\":\"toml\",\"req\":\"^0.8.14\"},{\"default_features\":false,\"name\":\"tracing\",\"req\":\"^0.1.30\"},{\"default_features\":false,\"features\":[\"env-filter\",\"fmt\",\"std\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^0.26\"}],\"features\":{\"__dnssec\":[],\"__h3\":[\"__quic\"],\"__https\":[\"__tls\"],\"__quic\":[\"dep:quinn\",\"__tls\"],\"__tls\":[\"dep:rustls\",\"dep:tokio-rustls\",\"tokio\"],\"backtrace\":[\"dep:backtrace\",\"hickory-proto/backtrace\"],\"default\":[\"system-config\",\"tokio\"],\"dnssec-aws-lc-rs\":[\"hickory-proto/dnssec-aws-lc-rs\",\"__dnssec\"],\"dnssec-ring\":[\"hickory-proto/dnssec-ring\",\"__dnssec\"],\"h3-aws-lc-rs\":[\"hickory-proto/h3-aws-lc-rs\",\"__h3\"],\"h3-ring\":[\"hickory-proto/h3-ring\",\"__h3\"],\"https-aws-lc-rs\":[\"hickory-proto/https-aws-lc-rs\",\"__https\"],\"https-ring\":[\"hickory-proto/https-ring\",\"__https\"],\"quic-aws-lc-rs\":[\"hickory-proto/quic-aws-lc-rs\",\"__quic\",\"quinn/rustls-aws-lc-rs\"],\"quic-ring\":[\"hickory-proto/quic-ring\",\"__quic\",\"quinn/rustls-ring\"],\"rustls-platform-verifier\":[\"hickory-proto/rustls-platform-verifier\"],\"serde\":[\"dep:serde\",\"hickory-proto/serde\"],\"system-config\":[\"dep:ipconfig\",\"dep:resolv-conf\"],\"tls-aws-lc-rs\":[\"hickory-proto/tls-aws-lc-rs\",\"__tls\"],\"tls-ring\":[\"hickory-proto/tls-ring\",\"__tls\"],\"tokio\":[\"dep:tokio\",\"tokio/rt\",\"hickory-proto/tokio\"],\"webpki-roots\":[\"dep:webpki-roots\",\"hickory-proto/webpki-roots\"]}}", "hkdf_0.12.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"blobby\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.2.2\"},{\"name\":\"hmac\",\"req\":\"^0.12.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"sha1\",\"req\":\"^0.10\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10\"}],\"features\":{\"std\":[\"hmac/std\"]}}", "hmac_0.12.1": "{\"dependencies\":[{\"features\":[\"mac\"],\"name\":\"digest\",\"req\":\"^0.10.3\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"digest\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.2.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"md-5\",\"req\":\"^0.10\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"sha-1\",\"req\":\"^0.10\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"streebog\",\"req\":\"^0.10\"}],\"features\":{\"reset\":[],\"std\":[\"digest/std\"]}}", - "home_0.5.11": "{\"dependencies\":[{\"features\":[\"Win32_Foundation\",\"Win32_UI_Shell\",\"Win32_System_Com\"],\"name\":\"windows-sys\",\"req\":\"^0.59\",\"target\":\"cfg(windows)\"}],\"features\":{}}", - "hostname_0.4.1": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(any(unix, target_os = \\\"redox\\\"))\"},{\"kind\":\"dev\",\"name\":\"similar-asserts\",\"req\":\"^1.6.1\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"kind\":\"dev\",\"name\":\"version-sync\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"windows-bindgen\",\"req\":\"^0.61\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"windows-link\",\"req\":\"^0.1.1\",\"target\":\"cfg(target_os = \\\"windows\\\")\"}],\"features\":{\"default\":[],\"set\":[]}}", + "home_0.5.12": "{\"dependencies\":[{\"features\":[\"Win32_Foundation\",\"Win32_UI_Shell\",\"Win32_System_Com\"],\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{}}", + "hostname_0.4.2": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(any(unix, target_os = \\\"redox\\\"))\"},{\"kind\":\"dev\",\"name\":\"similar-asserts\",\"req\":\"^1.6.1\"},{\"kind\":\"dev\",\"name\":\"version-sync\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"windows-bindgen\",\"req\":\"^0.65\"},{\"name\":\"windows-link\",\"req\":\"^0.2\",\"target\":\"cfg(target_os = \\\"windows\\\")\"}],\"features\":{\"default\":[],\"set\":[]}}", "http-body-util_0.1.3": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"http\",\"req\":\"^1\"},{\"name\":\"http-body\",\"req\":\"^1\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"macros\",\"rt\",\"sync\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"}],\"features\":{\"channel\":[\"dep:tokio\"],\"default\":[],\"full\":[\"channel\"]}}", "http-body_1.0.1": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"http\",\"req\":\"^1\"}],\"features\":{}}", "http-range-header_0.4.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.8.3\"}],\"features\":{}}", "http_0.2.12": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"fnv\",\"req\":\"^1.0.5\"},{\"kind\":\"dev\",\"name\":\"indexmap\",\"req\":\"<=1.8\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.7.0\"},{\"kind\":\"dev\",\"name\":\"seahash\",\"req\":\"^3.0.5\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{}}", - "http_1.3.1": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"fnv\",\"req\":\"^1.0.5\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.0\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "http_1.4.0": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.0\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "httparse_1.10.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.5\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "httpdate_1.0.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"}],\"features\":{}}", "hyper-rustls_0.27.7": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"http\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"hyper\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"client-legacy\",\"tokio\"],\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"server-auto\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.4\"},{\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"rustls\",\"req\":\"^0.23\"},{\"default_features\":false,\"features\":[\"tls12\"],\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rustls-pemfile\",\"req\":\"^2\"},{\"name\":\"rustls-platform-verifier\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"tokio\",\"req\":\"^1.0\"},{\"features\":[\"io-std\",\"macros\",\"net\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"tokio-rustls\",\"req\":\"^0.26\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"aws-lc-rs\":[\"rustls/aws_lc_rs\"],\"default\":[\"native-tokio\",\"http1\",\"tls12\",\"logging\",\"aws-lc-rs\"],\"fips\":[\"aws-lc-rs\",\"rustls/fips\"],\"http1\":[\"hyper-util/http1\"],\"http2\":[\"hyper-util/http2\"],\"logging\":[\"log\",\"tokio-rustls/logging\",\"rustls/logging\"],\"native-tokio\":[\"rustls-native-certs\"],\"ring\":[\"rustls/ring\"],\"tls12\":[\"tokio-rustls/tls12\",\"rustls/tls12\"],\"webpki-tokio\":[\"webpki-roots\"]}}", "hyper-timeout_0.5.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"name\":\"hyper\",\"req\":\"^1.1\"},{\"features\":[\"http1\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.1\"},{\"kind\":\"dev\",\"name\":\"hyper-tls\",\"req\":\"^0.6\"},{\"features\":[\"client-legacy\",\"http1\"],\"name\":\"hyper-util\",\"req\":\"^0.1.10\"},{\"features\":[\"client-legacy\",\"http1\",\"server\",\"server-graceful\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1.10\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"name\":\"tokio\",\"req\":\"^1.35\"},{\"features\":[\"io-std\",\"io-util\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.35\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"}],\"features\":{}}", "hyper-tls_0.6.0": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"name\":\"hyper\",\"req\":\"^1\"},{\"features\":[\"client-legacy\",\"tokio\"],\"name\":\"hyper-util\",\"req\":\"^0.1.0\"},{\"features\":[\"http1\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1.0\"},{\"name\":\"native-tls\",\"req\":\"^0.2.1\"},{\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"io-std\",\"macros\",\"io-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0.0\"},{\"name\":\"tokio-native-tls\",\"req\":\"^0.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"}],\"features\":{\"alpn\":[\"native-tls/alpn\"],\"vendored\":[\"native-tls/vendored\"]}}", - "hyper-util_0.1.16": "{\"dependencies\":[{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22\"},{\"name\":\"bytes\",\"req\":\"^1.7.1\"},{\"kind\":\"dev\",\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.16\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.16\"},{\"name\":\"http\",\"req\":\"^1.0\"},{\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"name\":\"hyper\",\"req\":\"^1.6.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.4.0\"},{\"name\":\"ipnet\",\"optional\":true,\"req\":\"^2.9\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.3\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.4\"},{\"kind\":\"dev\",\"name\":\"pnet_datalink\",\"req\":\"^0.35.0\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"macos\\\"))\"},{\"kind\":\"dev\",\"name\":\"pretty_env_logger\",\"req\":\"^0.5\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\">=0.5.9, <0.7\"},{\"name\":\"system-configuration\",\"optional\":true,\"req\":\"^0.6.1\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"macros\",\"test-util\",\"signal\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"},{\"name\":\"tower-service\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"windows-registry\",\"optional\":true,\"req\":\"^0.5\",\"target\":\"cfg(windows)\"}],\"features\":{\"__internal_happy_eyeballs_tests\":[],\"client\":[\"hyper/client\",\"tokio/net\",\"dep:tracing\",\"dep:futures-channel\",\"dep:tower-service\"],\"client-legacy\":[\"client\",\"dep:socket2\",\"tokio/sync\",\"dep:libc\",\"dep:futures-util\"],\"client-proxy\":[\"client\",\"dep:base64\",\"dep:ipnet\",\"dep:percent-encoding\"],\"client-proxy-system\":[\"dep:system-configuration\",\"dep:windows-registry\"],\"default\":[],\"full\":[\"client\",\"client-legacy\",\"client-proxy\",\"client-proxy-system\",\"server\",\"server-auto\",\"server-graceful\",\"service\",\"http1\",\"http2\",\"tokio\",\"tracing\"],\"http1\":[\"hyper/http1\"],\"http2\":[\"hyper/http2\"],\"server\":[\"hyper/server\"],\"server-auto\":[\"server\",\"http1\",\"http2\"],\"server-graceful\":[\"server\",\"tokio/sync\"],\"service\":[\"dep:tower-service\"],\"tokio\":[\"dep:tokio\",\"tokio/rt\",\"tokio/time\"],\"tracing\":[\"dep:tracing\"]}}", - "hyper_1.7.0": "{\"dependencies\":[{\"name\":\"atomic-waker\",\"optional\":true,\"req\":\"^1.1.2\"},{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"form_urlencoded\",\"req\":\"^1\"},{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\"},{\"features\":[\"sink\"],\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3\"},{\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3.31\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"alloc\",\"sink\"],\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4.2\"},{\"name\":\"http\",\"req\":\"^1\"},{\"name\":\"http-body\",\"req\":\"^1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"name\":\"httparse\",\"optional\":true,\"req\":\"^1.9\"},{\"name\":\"httpdate\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"itoa\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"pin-project-lite\",\"optional\":true,\"req\":\"^0.2.4\"},{\"kind\":\"dev\",\"name\":\"pin-project-lite\",\"req\":\"^0.2.4\"},{\"name\":\"pin-utils\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"pretty_env_logger\",\"req\":\"^0.5\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"const_generics\",\"const_new\"],\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.12\"},{\"kind\":\"dev\",\"name\":\"spmc\",\"req\":\"^0.3\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"fs\",\"macros\",\"net\",\"io-std\",\"io-util\",\"rt\",\"rt-multi-thread\",\"sync\",\"time\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.7.10\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"want\",\"optional\":true,\"req\":\"^0.3\"}],\"features\":{\"capi\":[],\"client\":[\"dep:want\",\"dep:pin-project-lite\",\"dep:smallvec\"],\"default\":[],\"ffi\":[\"dep:http-body-util\",\"dep:futures-util\"],\"full\":[\"client\",\"http1\",\"http2\",\"server\"],\"http1\":[\"dep:atomic-waker\",\"dep:futures-channel\",\"dep:futures-core\",\"dep:httparse\",\"dep:itoa\",\"dep:pin-utils\"],\"http2\":[\"dep:futures-channel\",\"dep:futures-core\",\"dep:h2\"],\"nightly\":[],\"server\":[\"dep:httpdate\",\"dep:pin-project-lite\",\"dep:smallvec\"],\"tracing\":[\"dep:tracing\"]}}", + "hyper-util_0.1.19": "{\"dependencies\":[{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22\"},{\"name\":\"bytes\",\"req\":\"^1.7.1\"},{\"kind\":\"dev\",\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.16\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.16\"},{\"name\":\"http\",\"req\":\"^1.0\"},{\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"name\":\"hyper\",\"req\":\"^1.8.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.4.0\"},{\"name\":\"ipnet\",\"optional\":true,\"req\":\"^2.9\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.3\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.4\"},{\"kind\":\"dev\",\"name\":\"pnet_datalink\",\"req\":\"^0.35.0\",\"target\":\"cfg(any(target_os = \\\"linux\\\", target_os = \\\"macos\\\"))\"},{\"kind\":\"dev\",\"name\":\"pretty_env_logger\",\"req\":\"^0.5\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\">=0.5.9, <0.7\"},{\"name\":\"system-configuration\",\"optional\":true,\"req\":\">=0.5, <0.7\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"macros\",\"test-util\",\"signal\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"},{\"name\":\"tower-layer\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"tower-service\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"tower-test\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"windows-registry\",\"optional\":true,\"req\":\">=0.3, <0.7\",\"target\":\"cfg(windows)\"}],\"features\":{\"__internal_happy_eyeballs_tests\":[],\"client\":[\"hyper/client\",\"tokio/net\",\"dep:tracing\",\"dep:futures-channel\",\"dep:tower-service\"],\"client-legacy\":[\"client\",\"dep:socket2\",\"tokio/sync\",\"dep:libc\",\"dep:futures-util\"],\"client-pool\":[\"client\",\"dep:futures-util\",\"dep:tower-layer\"],\"client-proxy\":[\"client\",\"dep:base64\",\"dep:ipnet\",\"dep:percent-encoding\"],\"client-proxy-system\":[\"dep:system-configuration\",\"dep:windows-registry\"],\"default\":[],\"full\":[\"client\",\"client-legacy\",\"client-pool\",\"client-proxy\",\"client-proxy-system\",\"server\",\"server-auto\",\"server-graceful\",\"service\",\"http1\",\"http2\",\"tokio\",\"tracing\"],\"http1\":[\"hyper/http1\"],\"http2\":[\"hyper/http2\"],\"server\":[\"hyper/server\"],\"server-auto\":[\"server\",\"http1\",\"http2\"],\"server-graceful\":[\"server\",\"tokio/sync\"],\"service\":[\"dep:tower-service\"],\"tokio\":[\"dep:tokio\",\"tokio/rt\",\"tokio/time\"],\"tracing\":[\"dep:tracing\"]}}", + "hyper_1.8.1": "{\"dependencies\":[{\"name\":\"atomic-waker\",\"optional\":true,\"req\":\"^1.1.2\"},{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"form_urlencoded\",\"req\":\"^1\"},{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\"},{\"features\":[\"sink\"],\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3\"},{\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3.31\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"alloc\",\"sink\"],\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4.2\"},{\"name\":\"http\",\"req\":\"^1\"},{\"name\":\"http-body\",\"req\":\"^1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"name\":\"httparse\",\"optional\":true,\"req\":\"^1.9\"},{\"name\":\"httpdate\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"itoa\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"pin-project-lite\",\"optional\":true,\"req\":\"^0.2.4\"},{\"kind\":\"dev\",\"name\":\"pin-project-lite\",\"req\":\"^0.2.4\"},{\"name\":\"pin-utils\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"pretty_env_logger\",\"req\":\"^0.5\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"const_generics\",\"const_new\"],\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.12\"},{\"kind\":\"dev\",\"name\":\"spmc\",\"req\":\"^0.3\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"fs\",\"macros\",\"net\",\"io-std\",\"io-util\",\"rt\",\"rt-multi-thread\",\"sync\",\"time\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.7.10\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"want\",\"optional\":true,\"req\":\"^0.3\"}],\"features\":{\"capi\":[],\"client\":[\"dep:want\",\"dep:pin-project-lite\",\"dep:smallvec\"],\"default\":[],\"ffi\":[\"dep:http-body-util\",\"dep:futures-util\"],\"full\":[\"client\",\"http1\",\"http2\",\"server\"],\"http1\":[\"dep:atomic-waker\",\"dep:futures-channel\",\"dep:futures-core\",\"dep:httparse\",\"dep:itoa\",\"dep:pin-utils\"],\"http2\":[\"dep:futures-channel\",\"dep:futures-core\",\"dep:h2\"],\"nightly\":[],\"server\":[\"dep:httpdate\",\"dep:pin-project-lite\",\"dep:smallvec\"],\"tracing\":[\"dep:tracing\"]}}", + "i18n-config_0.4.8": "{\"dependencies\":[{\"name\":\"basic-toml\",\"req\":\"^0.1\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"features\":[\"serde\"],\"name\":\"unic-langid\",\"req\":\"^0.9\"}],\"features\":{}}", + "i18n-embed-fl_0.9.4": "{\"dependencies\":[{\"name\":\"dashmap\",\"optional\":true,\"req\":\"^6.0\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"name\":\"find-crate\",\"req\":\"^0.6\"},{\"name\":\"fluent\",\"req\":\"^0.16\"},{\"name\":\"fluent-syntax\",\"req\":\"^0.11\"},{\"name\":\"i18n-config\",\"req\":\"^0.4.7\"},{\"features\":[\"fluent-system\",\"filesystem-assets\"],\"name\":\"i18n-embed\",\"req\":\"^0.15.4\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4\"},{\"name\":\"proc-macro-error2\",\"req\":\"^2.0.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rust-embed\",\"req\":\"^8.0\"},{\"name\":\"strsim\",\"req\":\"^0.11\"},{\"features\":[\"derive\",\"proc-macro\",\"parsing\",\"printing\",\"extra-traits\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"name\":\"unic-langid\",\"req\":\"^0.9\"}],\"features\":{\"dashmap\":[\"dep:dashmap\"]}}", + "i18n-embed-impl_0.8.4": "{\"dependencies\":[{\"name\":\"find-crate\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"i18n-config\",\"optional\":true,\"req\":\"^0.4.7\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rust-embed\",\"req\":\"^8.0\"},{\"features\":[\"derive\",\"proc-macro\",\"parsing\",\"printing\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{\"default\":[],\"fluent-system\":[\"i18n-config\",\"find-crate\",\"quote\"],\"gettext-system\":[\"i18n-config\",\"find-crate\",\"quote\"]}}", + "i18n-embed_0.15.4": "{\"dependencies\":[{\"name\":\"arc-swap\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"name\":\"fluent\",\"optional\":true,\"req\":\"^0.16\"},{\"name\":\"fluent-langneg\",\"req\":\"^0.13\"},{\"name\":\"fluent-syntax\",\"optional\":true,\"req\":\"^0.11\"},{\"name\":\"gettext\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"i18n-embed-impl\",\"optional\":true,\"req\":\"^0.8.4\"},{\"name\":\"intl-memoizer\",\"req\":\"^0.5\"},{\"name\":\"locale_config\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"maplit\",\"req\":\"^1.0\"},{\"name\":\"notify\",\"optional\":true,\"req\":\"^8.0.0\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4\"},{\"name\":\"rust-embed\",\"optional\":true,\"req\":\"^8.0\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^3.0\"},{\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"tr\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"unic-langid\",\"req\":\"^0.9\"},{\"name\":\"walkdir\",\"optional\":true,\"req\":\"^2.4\"},{\"features\":[\"Window\",\"Navigator\"],\"name\":\"web-sys\",\"optional\":true,\"req\":\"^0.3\"}],\"features\":{\"autoreload\":[\"notify\"],\"default\":[\"rust-embed\"],\"desktop-requester\":[\"locale_config\"],\"filesystem-assets\":[\"walkdir\"],\"fluent-system\":[\"fluent\",\"fluent-syntax\",\"parking_lot\",\"i18n-embed-impl\",\"i18n-embed-impl/fluent-system\",\"arc-swap\"],\"gettext-system\":[\"tr\",\"tr/gettext\",\"dep:gettext\",\"parking_lot\",\"i18n-embed-impl\",\"i18n-embed-impl/gettext-system\"],\"web-sys-requester\":[\"web-sys\"]}}", "iana-time-zone-haiku_0.1.2": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.79\"}],\"features\":{}}", - "iana-time-zone_0.1.63": "{\"dependencies\":[{\"name\":\"android_system_properties\",\"req\":\"^0.1.5\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"kind\":\"dev\",\"name\":\"chrono-tz\",\"req\":\"^0.10.1\"},{\"name\":\"core-foundation-sys\",\"req\":\"^0.8.6\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2.1\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2.1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"iana-time-zone-haiku\",\"req\":\"^0.1.1\",\"target\":\"cfg(target_os = \\\"haiku\\\")\"},{\"name\":\"js-sys\",\"req\":\"^0.3.66\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"log\",\"req\":\"^0.4.14\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"wasm-bindgen\",\"req\":\"^0.2.89\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.46\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"windows-core\",\"req\":\">=0.56, <=0.61\",\"target\":\"cfg(target_os = \\\"windows\\\")\"}],\"features\":{\"fallback\":[]}}", + "iana-time-zone_0.1.65": "{\"dependencies\":[{\"name\":\"android_system_properties\",\"req\":\"^0.1.5\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"kind\":\"dev\",\"name\":\"chrono-tz\",\"req\":\"^0.10.1\"},{\"name\":\"core-foundation-sys\",\"req\":\"^0.8.6\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2.1\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2.1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"iana-time-zone-haiku\",\"req\":\"^0.1.1\",\"target\":\"cfg(target_os = \\\"haiku\\\")\"},{\"name\":\"js-sys\",\"req\":\"^0.3.66\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"log\",\"req\":\"^0.4.14\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"wasm-bindgen\",\"req\":\"^0.2.89\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.46\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"windows-core\",\"req\":\">=0.56, <=0.62\",\"target\":\"cfg(target_os = \\\"windows\\\")\"}],\"features\":{\"fallback\":[]}}", "icu_collections_2.1.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2.3\"},{\"kind\":\"dev\",\"name\":\"iai\",\"req\":\"^0.1.1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"default_features\":false,\"features\":[\"zerovec\"],\"name\":\"potential_utf\",\"req\":\"^0.1.3\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"features\":[\"parse\"],\"kind\":\"dev\",\"name\":\"toml\",\"req\":\"^0.8.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"yoke\",\"req\":\"^0.8.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"zerofrom\",\"req\":\"^0.1.3\"},{\"default_features\":false,\"features\":[\"derive\",\"yoke\"],\"name\":\"zerovec\",\"req\":\"^0.11.3\"}],\"features\":{\"alloc\":[\"serde?/alloc\",\"zerovec/alloc\"],\"databake\":[\"dep:databake\",\"zerovec/databake\"],\"serde\":[\"dep:serde\",\"zerovec/serde\",\"potential_utf/serde\",\"alloc\"]}}", "icu_decimal_2.1.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"fixed_decimal\",\"req\":\"^0.7.0\"},{\"features\":[\"wasm_js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"icu_decimal_data\",\"optional\":true,\"req\":\"~2.1.1\"},{\"default_features\":false,\"name\":\"icu_locale\",\"optional\":true,\"req\":\"~2.1.1\"},{\"default_features\":false,\"name\":\"icu_locale_core\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"name\":\"icu_provider\",\"req\":\"^2.1.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_distr\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"writeable\",\"req\":\"^0.6.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"zerovec\",\"req\":\"^0.11.3\"}],\"features\":{\"alloc\":[\"serde?/alloc\",\"zerovec/alloc\"],\"compiled_data\":[\"dep:icu_decimal_data\",\"dep:icu_locale\",\"icu_locale?/compiled_data\",\"icu_provider/baked\"],\"datagen\":[\"serde\",\"dep:databake\",\"zerovec/databake\",\"icu_provider/export\",\"alloc\"],\"default\":[\"compiled_data\"],\"ryu\":[\"fixed_decimal/ryu\"],\"serde\":[\"dep:serde\",\"icu_provider/serde\",\"zerovec/serde\"]}}", "icu_decimal_data_2.1.1": "{\"dependencies\":[],\"features\":{}}", "icu_locale_2.1.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"icu_collections\",\"req\":\"~2.1.1\"},{\"default_features\":false,\"features\":[\"alloc\",\"zerovec\"],\"name\":\"icu_locale_core\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"name\":\"icu_locale_data\",\"optional\":true,\"req\":\"~2.1.1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"icu_provider\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"features\":[\"alloc\",\"zerovec\"],\"name\":\"potential_utf\",\"req\":\"^0.1.3\"},{\"default_features\":false,\"features\":[\"derive\",\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"features\":[\"alloc\",\"zerovec\"],\"name\":\"tinystr\",\"req\":\"^0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\",\"yoke\"],\"name\":\"zerovec\",\"req\":\"^0.11.3\"}],\"features\":{\"compiled_data\":[\"dep:icu_locale_data\",\"icu_provider/baked\"],\"datagen\":[\"serde\",\"dep:databake\",\"zerovec/databake\",\"icu_locale_core/databake\",\"tinystr/databake\",\"icu_collections/databake\",\"icu_provider/export\"],\"default\":[\"compiled_data\"],\"serde\":[\"dep:serde\",\"icu_locale_core/serde\",\"tinystr/serde\",\"zerovec/serde\",\"icu_provider/serde\",\"potential_utf/serde\",\"icu_collections/serde\"]}}", "icu_locale_core_2.1.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2.3\"},{\"default_features\":false,\"name\":\"litemap\",\"req\":\"^0.8.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"tinystr\",\"req\":\"^0.8.0\"},{\"default_features\":false,\"name\":\"writeable\",\"req\":\"^0.6.0\"},{\"default_features\":false,\"name\":\"zerovec\",\"optional\":true,\"req\":\"^0.11.3\"}],\"features\":{\"alloc\":[\"litemap/alloc\",\"tinystr/alloc\",\"writeable/alloc\",\"serde?/alloc\"],\"databake\":[\"dep:databake\",\"alloc\"],\"serde\":[\"dep:serde\",\"tinystr/serde\"],\"zerovec\":[\"dep:zerovec\",\"tinystr/zerovec\"]}}", - "icu_locale_data_2.1.1": "{\"dependencies\":[],\"features\":{}}", + "icu_locale_data_2.1.2": "{\"dependencies\":[],\"features\":{}}", "icu_normalizer_2.1.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"arraystring\",\"req\":\"^0.3.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"arrayvec\",\"req\":\"^0.7.2\"},{\"kind\":\"dev\",\"name\":\"atoi\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"detone\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"icu_collections\",\"req\":\"~2.1.1\"},{\"default_features\":false,\"name\":\"icu_normalizer_data\",\"optional\":true,\"req\":\"~2.1.1\"},{\"default_features\":false,\"name\":\"icu_properties\",\"optional\":true,\"req\":\"~2.1.1\"},{\"default_features\":false,\"name\":\"icu_provider\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"features\":[\"derive\",\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"smallvec\",\"req\":\"^1.10.0\"},{\"default_features\":false,\"name\":\"utf16_iter\",\"optional\":true,\"req\":\"^1.0.2\"},{\"default_features\":false,\"name\":\"utf8_iter\",\"optional\":true,\"req\":\"^1.0.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"write16\",\"optional\":true,\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"arrayvec\",\"smallvec\"],\"kind\":\"dev\",\"name\":\"write16\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"zerovec\",\"req\":\"^0.11.3\"}],\"features\":{\"compiled_data\":[\"dep:icu_normalizer_data\",\"icu_properties?/compiled_data\",\"icu_provider/baked\"],\"datagen\":[\"serde\",\"dep:databake\",\"icu_properties\",\"icu_collections/databake\",\"zerovec/databake\",\"icu_properties?/datagen\",\"icu_provider/export\"],\"default\":[\"compiled_data\",\"utf8_iter\",\"utf16_iter\"],\"experimental\":[],\"icu_properties\":[\"dep:icu_properties\"],\"serde\":[\"dep:serde\",\"icu_collections/serde\",\"zerovec/serde\",\"icu_properties?/serde\",\"icu_provider/serde\"],\"utf16_iter\":[\"dep:utf16_iter\",\"dep:write16\"],\"utf8_iter\":[\"dep:utf8_iter\"],\"write16\":[]}}", "icu_normalizer_data_2.1.1": "{\"dependencies\":[],\"features\":{}}", - "icu_properties_2.1.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"icu_collections\",\"req\":\"~2.1.1\"},{\"default_features\":false,\"features\":[\"zerovec\"],\"name\":\"icu_locale_core\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"name\":\"icu_properties_data\",\"optional\":true,\"req\":\"~2.1.1\"},{\"default_features\":false,\"name\":\"icu_provider\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"unicode-bidi\",\"optional\":true,\"req\":\"^0.3.11\"},{\"default_features\":false,\"features\":[\"yoke\",\"zerofrom\"],\"name\":\"zerotrie\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"derive\",\"yoke\"],\"name\":\"zerovec\",\"req\":\"^0.11.3\"}],\"features\":{\"alloc\":[\"zerovec/alloc\",\"icu_collections/alloc\",\"serde?/alloc\"],\"compiled_data\":[\"dep:icu_properties_data\",\"icu_provider/baked\"],\"datagen\":[\"serde\",\"dep:databake\",\"zerovec/databake\",\"icu_collections/databake\",\"icu_locale_core/databake\",\"zerotrie/databake\",\"icu_provider/export\"],\"default\":[\"compiled_data\"],\"serde\":[\"dep:serde\",\"icu_locale_core/serde\",\"zerovec/serde\",\"icu_collections/serde\",\"icu_provider/serde\",\"zerotrie/serde\"],\"unicode_bidi\":[\"dep:unicode-bidi\"]}}", - "icu_properties_data_2.1.1": "{\"dependencies\":[],\"features\":{}}", + "icu_properties_2.1.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"icu_collections\",\"req\":\"~2.1.1\"},{\"default_features\":false,\"features\":[\"zerovec\"],\"name\":\"icu_locale_core\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"name\":\"icu_properties_data\",\"optional\":true,\"req\":\"~2.1.2\"},{\"default_features\":false,\"name\":\"icu_provider\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"unicode-bidi\",\"optional\":true,\"req\":\"^0.3.11\"},{\"default_features\":false,\"features\":[\"yoke\",\"zerofrom\"],\"name\":\"zerotrie\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"derive\",\"yoke\"],\"name\":\"zerovec\",\"req\":\"^0.11.3\"}],\"features\":{\"alloc\":[\"zerovec/alloc\",\"icu_collections/alloc\",\"serde?/alloc\"],\"compiled_data\":[\"dep:icu_properties_data\",\"icu_provider/baked\"],\"datagen\":[\"serde\",\"dep:databake\",\"zerovec/databake\",\"icu_collections/databake\",\"icu_locale_core/databake\",\"zerotrie/databake\",\"icu_provider/export\"],\"default\":[\"compiled_data\"],\"serde\":[\"dep:serde\",\"icu_locale_core/serde\",\"zerovec/serde\",\"icu_collections/serde\",\"icu_provider/serde\",\"zerotrie/serde\"],\"unicode_bidi\":[\"dep:unicode-bidi\"]}}", + "icu_properties_data_2.1.2": "{\"dependencies\":[],\"features\":{}}", "icu_provider_2.1.1": "{\"dependencies\":[{\"name\":\"bincode\",\"optional\":true,\"req\":\"^1.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2.3\"},{\"name\":\"erased-serde\",\"optional\":true,\"req\":\"^0.4.0\"},{\"default_features\":false,\"name\":\"icu_locale_core\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.17\"},{\"default_features\":false,\"name\":\"postcard\",\"optional\":true,\"req\":\"^1.0.3\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.45\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"name\":\"stable_deref_trait\",\"optional\":true,\"req\":\"^1.2.0\"},{\"default_features\":false,\"name\":\"writeable\",\"optional\":true,\"req\":\"^0.6.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"yoke\",\"req\":\"^0.8.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"zerofrom\",\"req\":\"^0.1.3\"},{\"default_features\":false,\"name\":\"zerotrie\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"zerovec\",\"req\":\"^0.11.3\"}],\"features\":{\"alloc\":[\"icu_locale_core/alloc\",\"serde?/alloc\",\"yoke/alloc\",\"zerofrom/alloc\",\"zerovec/alloc\",\"zerotrie?/alloc\",\"dep:stable_deref_trait\",\"dep:writeable\"],\"baked\":[\"dep:zerotrie\",\"dep:writeable\"],\"deserialize_bincode_1\":[\"serde\",\"dep:bincode\",\"std\"],\"deserialize_json\":[\"serde\",\"dep:serde_json\"],\"deserialize_postcard_1\":[\"serde\",\"dep:postcard\"],\"export\":[\"serde\",\"dep:erased-serde\",\"dep:databake\",\"std\",\"sync\",\"dep:postcard\",\"zerovec/databake\"],\"logging\":[\"dep:log\"],\"serde\":[\"dep:serde\",\"yoke/serde\"],\"std\":[\"alloc\"],\"sync\":[],\"zerotrie\":[]}}", "ident_case_1.0.1": "{\"dependencies\":[],\"features\":{}}", - "idna_1.0.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.3\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1\"},{\"name\":\"idna_adapter\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"const_generics\"],\"name\":\"smallvec\",\"req\":\"^1.13.1\"},{\"kind\":\"dev\",\"name\":\"tester\",\"req\":\"^0.9\"},{\"name\":\"utf8_iter\",\"req\":\"^1.0.4\"}],\"features\":{\"alloc\":[],\"compiled_data\":[\"idna_adapter/compiled_data\"],\"default\":[\"std\",\"compiled_data\"],\"std\":[\"alloc\"]}}", + "idna_1.1.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.3\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1\"},{\"name\":\"idna_adapter\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"const_generics\"],\"name\":\"smallvec\",\"req\":\"^1.13.1\"},{\"kind\":\"dev\",\"name\":\"tester\",\"req\":\"^0.9\"},{\"name\":\"utf8_iter\",\"req\":\"^1.0.4\"}],\"features\":{\"alloc\":[],\"compiled_data\":[\"idna_adapter/compiled_data\"],\"default\":[\"std\",\"compiled_data\"],\"std\":[\"alloc\"]}}", "idna_adapter_1.2.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"icu_normalizer\",\"req\":\"^2\"},{\"default_features\":false,\"name\":\"icu_properties\",\"req\":\"^2\"}],\"features\":{\"compiled_data\":[\"icu_normalizer/compiled_data\",\"icu_properties/compiled_data\"]}}", - "ignore_0.4.23": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"bstr\",\"req\":\"^1.6.2\"},{\"kind\":\"dev\",\"name\":\"crossbeam-channel\",\"req\":\"^0.5.8\"},{\"name\":\"crossbeam-deque\",\"req\":\"^0.8.3\"},{\"name\":\"globset\",\"req\":\"^0.4.15\"},{\"name\":\"log\",\"req\":\"^0.4.20\"},{\"name\":\"memchr\",\"req\":\"^2.6.3\"},{\"default_features\":false,\"features\":[\"std\",\"perf\",\"syntax\",\"meta\",\"nfa\",\"hybrid\",\"dfa-onepass\"],\"name\":\"regex-automata\",\"req\":\"^0.4.0\"},{\"name\":\"same-file\",\"req\":\"^1.0.6\"},{\"name\":\"walkdir\",\"req\":\"^2.4.0\"},{\"name\":\"winapi-util\",\"req\":\"^0.1.2\",\"target\":\"cfg(windows)\"}],\"features\":{\"simd-accel\":[]}}", + "ignore_0.4.25": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"bstr\",\"req\":\"^1.6.2\"},{\"kind\":\"dev\",\"name\":\"crossbeam-channel\",\"req\":\"^0.5.15\"},{\"name\":\"crossbeam-deque\",\"req\":\"^0.8.3\"},{\"name\":\"globset\",\"req\":\"^0.4.18\"},{\"name\":\"log\",\"req\":\"^0.4.20\"},{\"name\":\"memchr\",\"req\":\"^2.6.3\"},{\"default_features\":false,\"features\":[\"std\",\"perf\",\"syntax\",\"meta\",\"nfa\",\"hybrid\",\"dfa-onepass\"],\"name\":\"regex-automata\",\"req\":\"^0.4.0\"},{\"name\":\"same-file\",\"req\":\"^1.0.6\"},{\"name\":\"walkdir\",\"req\":\"^2.4.0\"},{\"name\":\"winapi-util\",\"req\":\"^0.1.2\",\"target\":\"cfg(windows)\"}],\"features\":{\"simd-accel\":[]}}", "image_0.25.9": "{\"dependencies\":[{\"features\":[\"extern_crate_alloc\"],\"name\":\"bytemuck\",\"req\":\"^1.8.0\"},{\"name\":\"byteorder-lite\",\"req\":\"^0.1.0\"},{\"name\":\"color_quant\",\"optional\":true,\"req\":\"^1.1\"},{\"kind\":\"dev\",\"name\":\"crc32fast\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\"},{\"name\":\"dav1d\",\"optional\":true,\"req\":\"^0.10.3\"},{\"default_features\":false,\"name\":\"exr\",\"optional\":true,\"req\":\"^1.74.0\"},{\"name\":\"gif\",\"optional\":true,\"req\":\"^0.14.0\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3\"},{\"name\":\"image-webp\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"moxcms\",\"req\":\"^0.7.4\"},{\"name\":\"mp4parse\",\"optional\":true,\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"num-complex\",\"req\":\"^0.4\"},{\"name\":\"num-traits\",\"req\":\"^0.2.0\"},{\"name\":\"png\",\"optional\":true,\"req\":\"^0.18.0\"},{\"name\":\"qoi\",\"optional\":true,\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"ravif\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.7.0\"},{\"default_features\":false,\"name\":\"rgb\",\"optional\":true,\"req\":\"^0.8.48\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.214\"},{\"name\":\"tiff\",\"optional\":true,\"req\":\"^0.10.3\"},{\"default_features\":false,\"name\":\"zune-core\",\"optional\":true,\"req\":\"^0.5.0\"},{\"name\":\"zune-jpeg\",\"optional\":true,\"req\":\"^0.5.5\"}],\"features\":{\"avif\":[\"dep:ravif\",\"dep:rgb\"],\"avif-native\":[\"dep:mp4parse\",\"dep:dav1d\"],\"benchmarks\":[],\"bmp\":[],\"color_quant\":[\"dep:color_quant\"],\"dds\":[],\"default\":[\"rayon\",\"default-formats\"],\"default-formats\":[\"avif\",\"bmp\",\"dds\",\"exr\",\"ff\",\"gif\",\"hdr\",\"ico\",\"jpeg\",\"png\",\"pnm\",\"qoi\",\"tga\",\"tiff\",\"webp\"],\"exr\":[\"dep:exr\"],\"ff\":[],\"gif\":[\"dep:gif\",\"dep:color_quant\"],\"hdr\":[],\"ico\":[\"bmp\",\"png\"],\"jpeg\":[\"dep:zune-core\",\"dep:zune-jpeg\"],\"nasm\":[\"ravif?/asm\"],\"png\":[\"dep:png\"],\"pnm\":[],\"qoi\":[\"dep:qoi\"],\"rayon\":[\"dep:rayon\",\"ravif?/threading\",\"exr?/rayon\"],\"serde\":[\"dep:serde\"],\"tga\":[],\"tiff\":[\"dep:tiff\"],\"webp\":[\"dep:image-webp\"]}}", "impl-more_0.1.9": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"eyre\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"}],\"features\":{}}", "include_dir_0.7.4": "{\"dependencies\":[{\"name\":\"glob\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"include_dir_macros\",\"req\":\"^0.7.4\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"default\":[],\"metadata\":[\"include_dir_macros/metadata\"],\"nightly\":[\"include_dir_macros/nightly\"]}}", "include_dir_macros_0.7.4": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"}],\"features\":{\"metadata\":[],\"nightly\":[]}}", - "indenter_0.3.3": "{\"dependencies\":[],\"features\":{\"default\":[],\"std\":[]}}", + "indenter_0.3.4": "{\"dependencies\":[],\"features\":{\"default\":[],\"std\":[]}}", "indexmap_1.9.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"autocfg\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"fxhash\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"features\":[\"raw\"],\"name\":\"hashbrown\",\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.3\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.4.1\"},{\"name\":\"rustc-rayon\",\"optional\":true,\"package\":\"rustc-rayon\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\"}],\"features\":{\"serde-1\":[\"serde\"],\"std\":[],\"test_debug\":[],\"test_low_transition_point\":[]}}", - "indexmap_2.12.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"borsh\",\"optional\":true,\"req\":\"^1.2\"},{\"default_features\":false,\"name\":\"equivalent\",\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"hashbrown\",\"req\":\"^0.16\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.9\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"sval\",\"optional\":true,\"req\":\"^2\"}],\"features\":{\"default\":[\"std\"],\"serde\":[\"dep:serde_core\",\"dep:serde\"],\"std\":[],\"test_debug\":[]}}", - "indoc_2.0.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.49\"},{\"kind\":\"dev\",\"name\":\"unindent\",\"req\":\"^0.2.3\"}],\"features\":{}}", + "indexmap_2.13.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"borsh\",\"optional\":true,\"req\":\"^1.2\"},{\"default_features\":false,\"name\":\"equivalent\",\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"hashbrown\",\"req\":\"^0.16.1\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.9\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"sval\",\"optional\":true,\"req\":\"^2\"}],\"features\":{\"default\":[\"std\"],\"serde\":[\"dep:serde_core\",\"dep:serde\"],\"std\":[],\"test_debug\":[]}}", + "indoc_2.0.7": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"},{\"kind\":\"dev\",\"name\":\"unindent\",\"req\":\"^0.2.3\"}],\"features\":{}}", "inotify-sys_0.1.5": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2\"}],\"features\":{}}", "inotify_0.11.0": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.1\"},{\"name\":\"inotify-sys\",\"req\":\"^0.1.3\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"maplit\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\"},{\"features\":[\"net\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.0.1\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0.1\"}],\"features\":{\"default\":[\"stream\"],\"stream\":[\"futures-core\",\"tokio\"]}}", "inout_0.1.4": "{\"dependencies\":[{\"name\":\"block-padding\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"generic-array\",\"req\":\"^0.14\"}],\"features\":{\"std\":[\"block-padding/std\"]}}", - "insta_1.46.0": "{\"dependencies\":[{\"features\":[\"derive\",\"env\"],\"name\":\"clap\",\"optional\":true,\"req\":\"^4.1\"},{\"default_features\":false,\"name\":\"console\",\"optional\":true,\"req\":\"^0.15.4\"},{\"name\":\"csv\",\"optional\":true,\"req\":\"^1.1.6\"},{\"name\":\"globset\",\"optional\":true,\"req\":\">=0.4.6, <0.4.17\"},{\"name\":\"once_cell\",\"req\":\"^1.20.2\"},{\"name\":\"pest\",\"optional\":true,\"req\":\"^2.1.3\"},{\"name\":\"pest_derive\",\"optional\":true,\"req\":\"^2.1.0\"},{\"default_features\":false,\"features\":[\"std\",\"unicode\"],\"name\":\"regex\",\"optional\":true,\"req\":\"^1.6.0\"},{\"name\":\"ron\",\"optional\":true,\"req\":\"^0.12.0\"},{\"kind\":\"dev\",\"name\":\"rustc_version\",\"req\":\"^0.4.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.117\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.117\"},{\"features\":[\"inline\"],\"name\":\"similar\",\"req\":\"^2.1.0\"},{\"kind\":\"dev\",\"name\":\"similar-asserts\",\"req\":\"^1.4.2\"},{\"name\":\"tempfile\",\"req\":\"^3\"},{\"features\":[\"serde\",\"parse\",\"display\"],\"name\":\"toml_edit\",\"optional\":true,\"req\":\"^0.23.0\"},{\"name\":\"toml_writer\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"walkdir\",\"optional\":true,\"req\":\"^2.3.1\"}],\"features\":{\"_cargo_insta_internal\":[\"clap\"],\"colors\":[\"console\"],\"csv\":[\"dep:csv\",\"serde\"],\"default\":[\"colors\"],\"filters\":[\"regex\"],\"glob\":[\"walkdir\",\"globset\"],\"json\":[\"serde\"],\"redactions\":[\"pest\",\"pest_derive\",\"serde\"],\"ron\":[\"dep:ron\",\"serde\"],\"toml\":[\"dep:toml_edit\",\"dep:toml_writer\",\"serde\"],\"yaml\":[\"serde\"]}}", - "instability_0.3.9": "{\"dependencies\":[{\"name\":\"darling\",\"req\":\"^0.20.10\"},{\"name\":\"indoc\",\"req\":\"^2.0.5\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.92\"},{\"name\":\"quote\",\"req\":\"^1.0.37\"},{\"features\":[\"derive\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0.90\"}],\"features\":{}}", - "inventory_0.3.20": "{\"dependencies\":[{\"name\":\"rustversion\",\"req\":\"^1.0\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.89\"}],\"features\":{}}", + "insta_1.46.2": "{\"dependencies\":[{\"features\":[\"derive\",\"env\"],\"name\":\"clap\",\"optional\":true,\"req\":\"^4.1\"},{\"default_features\":false,\"name\":\"console\",\"optional\":true,\"req\":\"^0.15.4\"},{\"name\":\"csv\",\"optional\":true,\"req\":\"^1.1.6\"},{\"name\":\"globset\",\"optional\":true,\"req\":\"^0.4.6\"},{\"name\":\"once_cell\",\"req\":\"^1.20.2\"},{\"name\":\"pest\",\"optional\":true,\"req\":\"^2.1.3\"},{\"name\":\"pest_derive\",\"optional\":true,\"req\":\"^2.1.0\"},{\"default_features\":false,\"features\":[\"std\",\"unicode\"],\"name\":\"regex\",\"optional\":true,\"req\":\"^1.6.0\"},{\"name\":\"ron\",\"optional\":true,\"req\":\"^0.12.0\"},{\"kind\":\"dev\",\"name\":\"rustc_version\",\"req\":\"^0.4.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.117\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.117\"},{\"features\":[\"inline\"],\"name\":\"similar\",\"req\":\"^2.1.0\"},{\"kind\":\"dev\",\"name\":\"similar-asserts\",\"req\":\"^1.4.2\"},{\"name\":\"tempfile\",\"req\":\"^3\"},{\"features\":[\"serde\",\"parse\",\"display\"],\"name\":\"toml_edit\",\"optional\":true,\"req\":\"^0.23.0\"},{\"name\":\"toml_writer\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"walkdir\",\"optional\":true,\"req\":\"^2.3.1\"}],\"features\":{\"_cargo_insta_internal\":[\"clap\"],\"colors\":[\"console\"],\"csv\":[\"dep:csv\",\"serde\"],\"default\":[\"colors\"],\"filters\":[\"regex\"],\"glob\":[\"walkdir\",\"globset\"],\"json\":[\"serde\"],\"redactions\":[\"pest\",\"pest_derive\",\"serde\"],\"ron\":[\"dep:ron\",\"serde\"],\"toml\":[\"dep:toml_edit\",\"dep:toml_writer\",\"serde\"],\"yaml\":[\"serde\"]}}", + "instability_0.3.11": "{\"dependencies\":[{\"name\":\"darling\",\"req\":\"^0.23\"},{\"name\":\"indoc\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"name\":\"quote\",\"req\":\"^1.0.25\"},{\"features\":[\"derive\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{}}", + "intl-memoizer_0.5.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"fluent-langneg\",\"req\":\"^0.13\"},{\"kind\":\"dev\",\"name\":\"intl_pluralrules\",\"req\":\"^7.0\"},{\"name\":\"type-map\",\"req\":\"^0.5\"},{\"name\":\"unic-langid\",\"req\":\"^0.9\"}],\"features\":{}}", + "intl_pluralrules_7.0.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"name\":\"unic-langid\",\"req\":\"^0.9\"},{\"features\":[\"macros\"],\"kind\":\"dev\",\"name\":\"unic-langid\",\"req\":\"^0.9\"}],\"features\":{}}", + "inventory_0.3.21": "{\"dependencies\":[{\"name\":\"rustversion\",\"req\":\"^1.0\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.89\"}],\"features\":{}}", + "io_tee_0.1.1": "{\"dependencies\":[],\"features\":{}}", "ipconfig_0.3.2": "{\"dependencies\":[{\"name\":\"socket2\",\"req\":\"^0.5.1\",\"target\":\"cfg(windows)\"},{\"name\":\"widestring\",\"req\":\"^1.0.2\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Networking_WinSock\",\"Win32_System_Registry\"],\"name\":\"windows-sys\",\"req\":\"^0.48.0\",\"target\":\"cfg(windows)\"},{\"name\":\"winreg\",\"optional\":true,\"req\":\"^0.50.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"computer\":[\"winreg\"],\"default\":[\"computer\"]}}", "ipnet_2.11.0": "{\"dependencies\":[{\"name\":\"heapless\",\"optional\":true,\"req\":\"^0\"},{\"name\":\"schemars\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"package\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1\"}],\"features\":{\"default\":[\"std\"],\"json\":[\"serde\",\"schemars\"],\"ser_as_str\":[\"heapless\"],\"std\":[]}}", - "iri-string_0.7.8": "{\"dependencies\":[{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.4.1\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.103\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.104\"}],\"features\":{\"alloc\":[\"serde?/alloc\"],\"default\":[\"std\"],\"std\":[\"alloc\",\"memchr?/std\",\"serde?/std\"]}}", - "is-terminal_0.4.16": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"atty\",\"req\":\"^0.2.14\"},{\"name\":\"hermit-abi\",\"req\":\"^0.5.0\",\"target\":\"cfg(target_os = \\\"hermit\\\")\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\"))\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.110\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\"))\"},{\"features\":[\"termios\"],\"kind\":\"dev\",\"name\":\"rustix\",\"req\":\"^1.0.0\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\"))\"},{\"features\":[\"stdio\"],\"kind\":\"dev\",\"name\":\"rustix\",\"req\":\"^1.0.0\",\"target\":\"cfg(not(any(windows, target_os = \\\"hermit\\\", target_os = \\\"unknown\\\")))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Storage_FileSystem\",\"Win32_System_Console\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <0.60\",\"target\":\"cfg(windows)\"}],\"features\":{}}", + "iri-string_0.7.10": "{\"dependencies\":[{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.4.1\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.103\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.104\"}],\"features\":{\"alloc\":[\"serde?/alloc\"],\"default\":[\"std\"],\"std\":[\"alloc\",\"memchr?/std\",\"serde?/std\"]}}", + "is-terminal_0.4.17": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"atty\",\"req\":\"^0.2.14\"},{\"name\":\"hermit-abi\",\"req\":\"^0.5.0\",\"target\":\"cfg(target_os = \\\"hermit\\\")\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\"))\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.110\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\"))\"},{\"features\":[\"termios\"],\"kind\":\"dev\",\"name\":\"rustix\",\"req\":\"^1.0.0\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\"))\"},{\"features\":[\"stdio\"],\"kind\":\"dev\",\"name\":\"rustix\",\"req\":\"^1.0.0\",\"target\":\"cfg(not(any(windows, target_os = \\\"hermit\\\", target_os = \\\"unknown\\\")))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Storage_FileSystem\",\"Win32_System_Console\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "is_ci_1.2.0": "{\"dependencies\":[],\"features\":{}}", - "is_terminal_polyfill_1.70.1": "{\"dependencies\":[],\"features\":{\"default\":[]}}", + "is_terminal_polyfill_1.70.2": "{\"dependencies\":[],\"features\":{\"default\":[]}}", "itertools_0.10.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"= 0\"},{\"default_features\":false,\"name\":\"either\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"permutohedron\",\"req\":\"^0.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.7\"}],\"features\":{\"default\":[\"use_std\"],\"use_alloc\":[],\"use_std\":[\"use_alloc\",\"either/use_std\"]}}", "itertools_0.13.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"name\":\"either\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"permutohedron\",\"req\":\"^0.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.7\"}],\"features\":{\"default\":[\"use_std\"],\"use_alloc\":[],\"use_std\":[\"use_alloc\",\"either/use_std\"]}}", "itertools_0.14.0": "{\"dependencies\":[{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"name\":\"either\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"permutohedron\",\"req\":\"^0.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.7\"}],\"features\":{\"default\":[\"use_std\"],\"use_alloc\":[],\"use_std\":[\"use_alloc\",\"either/use_std\"]}}", - "itoa_1.0.15": "{\"dependencies\":[{\"name\":\"no-panic\",\"optional\":true,\"req\":\"^0.1\"}],\"features\":{}}", - "jiff-static_0.2.15": "{\"dependencies\":[{\"name\":\"jiff-tzdb\",\"optional\":true,\"req\":\"^0.1.4\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.93\"},{\"name\":\"quote\",\"req\":\"^1.0.38\"},{\"name\":\"syn\",\"req\":\"^2.0.98\"}],\"features\":{\"default\":[],\"perf-inline\":[],\"tz-fat\":[],\"tzdb\":[\"dep:jiff-tzdb\"]}}", - "jiff_0.2.15": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.81\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4.38\"},{\"kind\":\"dev\",\"name\":\"chrono-tz\",\"req\":\"^0.10.0\"},{\"kind\":\"dev\",\"name\":\"hifitime\",\"req\":\"^3.9.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"humantime\",\"req\":\"^2.1.0\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.39.0\"},{\"name\":\"jiff-static\",\"req\":\"=0.2.15\",\"target\":\"cfg(any())\"},{\"name\":\"jiff-static\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"jiff-tzdb\",\"optional\":true,\"req\":\"^0.1.4\"},{\"name\":\"jiff-tzdb-platform\",\"optional\":true,\"req\":\"^0.1.3\",\"target\":\"cfg(any(windows, target_family = \\\"wasm\\\"))\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.50\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.21\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.21\"},{\"default_features\":false,\"name\":\"portable-atomic\",\"req\":\"^1.10.0\",\"target\":\"cfg(not(target_has_atomic = \\\"ptr\\\"))\"},{\"default_features\":false,\"name\":\"portable-atomic-util\",\"req\":\"^0.2.4\",\"target\":\"cfg(not(target_has_atomic = \\\"ptr\\\"))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.203\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.203\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.117\"},{\"kind\":\"dev\",\"name\":\"serde_yaml\",\"req\":\"^0.9.34\"},{\"kind\":\"dev\",\"name\":\"tabwriter\",\"req\":\"^1.4.0\"},{\"features\":[\"local-offset\",\"macros\",\"parsing\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.36\"},{\"kind\":\"dev\",\"name\":\"tzfile\",\"req\":\"^0.1.3\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.5.0\"},{\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.70\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"features\":[\"Win32_Foundation\",\"Win32_System_Time\"],\"name\":\"windows-sys\",\"optional\":true,\"req\":\">=0.52.0, <=0.59\",\"target\":\"cfg(windows)\"}],\"features\":{\"alloc\":[\"serde?/alloc\",\"portable-atomic-util/alloc\"],\"default\":[\"std\",\"tz-system\",\"tz-fat\",\"tzdb-bundle-platform\",\"tzdb-zoneinfo\",\"tzdb-concatenated\",\"perf-inline\"],\"js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"],\"logging\":[\"dep:log\"],\"perf-inline\":[],\"serde\":[\"dep:serde\"],\"static\":[\"static-tz\",\"jiff-static?/tzdb\"],\"static-tz\":[\"dep:jiff-static\"],\"std\":[\"alloc\",\"log?/std\",\"serde?/std\"],\"tz-fat\":[\"jiff-static?/tz-fat\"],\"tz-system\":[\"std\",\"dep:windows-sys\"],\"tzdb-bundle-always\":[\"dep:jiff-tzdb\",\"alloc\"],\"tzdb-bundle-platform\":[\"dep:jiff-tzdb-platform\",\"alloc\"],\"tzdb-concatenated\":[\"std\"],\"tzdb-zoneinfo\":[\"std\"]}}", + "itoa_1.0.17": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\",\"target\":\"cfg(not(miri))\"},{\"name\":\"no-panic\",\"optional\":true,\"req\":\"^0.1\"}],\"features\":{}}", + "jiff-static_0.2.18": "{\"dependencies\":[{\"name\":\"jiff-tzdb\",\"optional\":true,\"req\":\"^0.1.4\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.93\"},{\"name\":\"quote\",\"req\":\"^1.0.38\"},{\"name\":\"syn\",\"req\":\"^2.0.98\"}],\"features\":{\"default\":[],\"perf-inline\":[],\"tz-fat\":[],\"tzdb\":[\"dep:jiff-tzdb\"]}}", + "jiff_0.2.18": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.81\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4.38\"},{\"kind\":\"dev\",\"name\":\"chrono-tz\",\"req\":\"^0.10.0\"},{\"kind\":\"dev\",\"name\":\"hifitime\",\"req\":\"^3.9.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"humantime\",\"req\":\"^2.1.0\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.39.0\"},{\"name\":\"jiff-static\",\"req\":\"=0.2.18\",\"target\":\"cfg(any())\"},{\"name\":\"jiff-static\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"jiff-tzdb\",\"optional\":true,\"req\":\"^0.1.5\"},{\"name\":\"jiff-tzdb-platform\",\"optional\":true,\"req\":\"^0.1.3\",\"target\":\"cfg(any(windows, target_family = \\\"wasm\\\"))\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.50\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.21\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.21\"},{\"default_features\":false,\"name\":\"portable-atomic\",\"req\":\"^1.10.0\",\"target\":\"cfg(not(target_has_atomic = \\\"ptr\\\"))\"},{\"default_features\":false,\"name\":\"portable-atomic-util\",\"req\":\"^0.2.4\",\"target\":\"cfg(not(target_has_atomic = \\\"ptr\\\"))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.203\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.221\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.117\"},{\"kind\":\"dev\",\"name\":\"serde_yaml\",\"req\":\"^0.9.34\"},{\"kind\":\"dev\",\"name\":\"tabwriter\",\"req\":\"^1.4.0\"},{\"features\":[\"local-offset\",\"macros\",\"parsing\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.36\"},{\"kind\":\"dev\",\"name\":\"tzfile\",\"req\":\"^0.1.3\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.5.0\"},{\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2.70\",\"target\":\"cfg(all(any(target_arch = \\\"wasm32\\\", target_arch = \\\"wasm64\\\"), target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"features\":[\"Win32_Foundation\",\"Win32_System_Time\"],\"name\":\"windows-sys\",\"optional\":true,\"req\":\">=0.52.0, <=0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"alloc\":[\"serde_core?/alloc\",\"portable-atomic-util/alloc\"],\"default\":[\"std\",\"tz-system\",\"tz-fat\",\"tzdb-bundle-platform\",\"tzdb-zoneinfo\",\"tzdb-concatenated\",\"perf-inline\"],\"js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"],\"logging\":[\"dep:log\"],\"perf-inline\":[],\"serde\":[\"dep:serde_core\"],\"static\":[\"static-tz\",\"jiff-static?/tzdb\"],\"static-tz\":[\"dep:jiff-static\"],\"std\":[\"alloc\",\"log?/std\",\"serde_core?/std\"],\"tz-fat\":[\"jiff-static?/tz-fat\"],\"tz-system\":[\"std\",\"dep:windows-sys\"],\"tzdb-bundle-always\":[\"dep:jiff-tzdb\",\"alloc\"],\"tzdb-bundle-platform\":[\"dep:jiff-tzdb-platform\",\"alloc\"],\"tzdb-concatenated\":[\"std\"],\"tzdb-zoneinfo\":[\"std\"]}}", "jni-sys_0.3.0": "{\"dependencies\":[],\"features\":{}}", "jni_0.21.1": "{\"dependencies\":[{\"name\":\"cesu8\",\"req\":\"^1.1.0\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"combine\",\"req\":\"^4.1.0\"},{\"name\":\"java-locator\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"jni-sys\",\"req\":\"^0.3.0\"},{\"name\":\"libloading\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"name\":\"thiserror\",\"req\":\"^1.0.20\"},{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rusty-fork\",\"req\":\"^0.3.0\"},{\"kind\":\"build\",\"name\":\"walkdir\",\"req\":\"^2\"},{\"features\":[\"Win32_Globalization\"],\"name\":\"windows-sys\",\"req\":\"^0.45.0\",\"target\":\"cfg(windows)\"},{\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.13.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[],\"invocation\":[\"java-locator\",\"libloading\"]}}", "jobserver_0.1.34": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"getrandom\",\"req\":\"^0.3.2\",\"target\":\"cfg(windows)\"},{\"name\":\"libc\",\"req\":\"^0.2.171\",\"target\":\"cfg(unix)\"},{\"features\":[\"fs\"],\"kind\":\"dev\",\"name\":\"nix\",\"req\":\"^0.28.0\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.10.1\"}],\"features\":{}}", - "js-sys_0.3.77": "{\"dependencies\":[{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.100\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"wasm-bindgen/std\"]}}", + "js-sys_0.3.85": "{\"dependencies\":[{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.108\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"wasm-bindgen/std\"]}}", "keyring_3.6.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"base64\",\"req\":\"^0.22\"},{\"name\":\"byteorder\",\"optional\":true,\"req\":\"^1.2\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"features\":[\"derive\",\"wrap_help\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4\"},{\"name\":\"dbus-secret-service\",\"optional\":true,\"req\":\"^4.0.0-rc.1\",\"target\":\"cfg(target_os = \\\"openbsd\\\")\"},{\"name\":\"dbus-secret-service\",\"optional\":true,\"req\":\"^4.0.0-rc.2\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"dbus-secret-service\",\"optional\":true,\"req\":\"^4.0.1\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.5\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2\"},{\"features\":[\"std\"],\"name\":\"linux-keyutils\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"log\",\"req\":\"^0.4.22\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10.66\"},{\"kind\":\"dev\",\"name\":\"rpassword\",\"req\":\"^7\"},{\"kind\":\"dev\",\"name\":\"rprompt\",\"req\":\"^2\"},{\"name\":\"secret-service\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"name\":\"secret-service\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"secret-service\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"openbsd\\\")\"},{\"name\":\"security-framework\",\"optional\":true,\"req\":\"^2\",\"target\":\"cfg(target_os = \\\"ios\\\")\"},{\"name\":\"security-framework\",\"optional\":true,\"req\":\"^3\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"kind\":\"dev\",\"name\":\"whoami\",\"req\":\"^1.5\"},{\"features\":[\"Win32_Foundation\",\"Win32_Security_Credentials\"],\"name\":\"windows-sys\",\"optional\":true,\"req\":\"^0.60\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"zbus\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"name\":\"zbus\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"zbus\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"openbsd\\\")\"},{\"name\":\"zeroize\",\"req\":\"^1.8.1\",\"target\":\"cfg(target_os = \\\"windows\\\")\"}],\"features\":{\"apple-native\":[\"dep:security-framework\"],\"async-io\":[\"zbus?/async-io\"],\"async-secret-service\":[\"dep:secret-service\",\"dep:zbus\"],\"crypto-openssl\":[\"dbus-secret-service?/crypto-openssl\",\"secret-service?/crypto-openssl\"],\"crypto-rust\":[\"dbus-secret-service?/crypto-rust\",\"secret-service?/crypto-rust\"],\"linux-native\":[\"dep:linux-keyutils\"],\"linux-native-async-persistent\":[\"linux-native\",\"async-secret-service\"],\"linux-native-sync-persistent\":[\"linux-native\",\"sync-secret-service\"],\"sync-secret-service\":[\"dep:dbus-secret-service\"],\"tokio\":[\"zbus?/tokio\"],\"vendored\":[\"dbus-secret-service?/vendored\",\"openssl?/vendored\"],\"windows-native\":[\"dep:windows-sys\",\"dep:byteorder\"]}}", "kqueue-sys_1.0.4": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^1.2.1\"},{\"name\":\"libc\",\"req\":\"^0.2.74\"}],\"features\":{}}", "kqueue_1.1.1": "{\"dependencies\":[{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"dhat\",\"req\":\"^0.3.2\"},{\"name\":\"kqueue-sys\",\"req\":\"^1.0.4\"},{\"name\":\"libc\",\"req\":\"^0.2.17\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\"}],\"features\":{}}", @@ -881,18 +909,18 @@ "landlock_0.4.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"enumflags2\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"libc\",\"req\":\"^0.2.175\"},{\"kind\":\"dev\",\"name\":\"strum\",\"req\":\"^0.26\"},{\"kind\":\"dev\",\"name\":\"strum_macros\",\"req\":\"^0.26\"},{\"name\":\"thiserror\",\"req\":\"^2.0\"}],\"features\":{}}", "language-tags_0.3.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{}}", "lazy_static_1.5.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"features\":[\"once\"],\"name\":\"spin\",\"optional\":true,\"req\":\"^0.9.8\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1\"}],\"features\":{\"spin_no_std\":[\"spin\"]}}", - "libc_0.2.177": "{\"dependencies\":[{\"name\":\"rustc-std-workspace-core\",\"optional\":true,\"req\":\"^1.0.1\"}],\"features\":{\"align\":[],\"const-extern-fn\":[],\"default\":[\"std\"],\"extra_traits\":[],\"rustc-dep-of-std\":[\"align\",\"rustc-std-workspace-core\"],\"std\":[],\"use_std\":[\"std\"]}}", - "libdbus-sys_0.2.6": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"optional\":true,\"req\":\"^1.0.78\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"optional\":true,\"req\":\"^0.3\"}],\"features\":{\"default\":[\"pkg-config\"],\"vendored\":[\"cc\"]}}", + "libc_0.2.180": "{\"dependencies\":[{\"name\":\"rustc-std-workspace-core\",\"optional\":true,\"req\":\"^1.0.1\"}],\"features\":{\"align\":[],\"const-extern-fn\":[],\"default\":[\"std\"],\"extra_traits\":[],\"rustc-dep-of-std\":[\"align\",\"rustc-std-workspace-core\"],\"std\":[],\"use_std\":[\"std\"]}}", + "libdbus-sys_0.2.7": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"optional\":true,\"req\":\"^1.0.78\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"optional\":true,\"req\":\"^0.3\"}],\"features\":{\"default\":[\"pkg-config\"],\"vendored\":[\"cc\"]}}", "libloading_0.8.9": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"name\":\"windows-link\",\"req\":\"^0.2\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_Foundation\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "libm_0.2.16": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"no-panic\",\"req\":\"^0.1.35\"}],\"features\":{\"arch\":[],\"default\":[\"arch\"],\"force-soft-floats\":[],\"unstable\":[\"unstable-intrinsics\",\"unstable-float\"],\"unstable-float\":[],\"unstable-intrinsics\":[],\"unstable-public-internals\":[]}}", - "libredox_0.1.6": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"name\":\"ioslice\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"redox_syscall\",\"optional\":true,\"req\":\"^0.5\"}],\"features\":{\"call\":[],\"default\":[\"call\",\"std\",\"redox_syscall\"],\"mkns\":[\"ioslice\"],\"std\":[]}}", + "libredox_0.1.12": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"name\":\"ioslice\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"redox_syscall\",\"optional\":true,\"req\":\"^0.7\"}],\"features\":{\"call\":[],\"default\":[\"call\",\"std\",\"redox_syscall\"],\"mkns\":[\"ioslice\"],\"std\":[]}}", "libsqlite3-sys_0.30.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"runtime\"],\"kind\":\"build\",\"name\":\"bindgen\",\"optional\":true,\"req\":\"^0.69\"},{\"kind\":\"build\",\"name\":\"cc\",\"optional\":true,\"req\":\"^1.1.6\"},{\"name\":\"openssl-sys\",\"optional\":true,\"req\":\"^0.9.103\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"optional\":true,\"req\":\"^0.3.19\"},{\"kind\":\"build\",\"name\":\"prettyplease\",\"optional\":true,\"req\":\"^0.2.20\"},{\"default_features\":false,\"kind\":\"build\",\"name\":\"quote\",\"optional\":true,\"req\":\"^1.0.36\"},{\"features\":[\"full\",\"extra-traits\",\"visit-mut\"],\"kind\":\"build\",\"name\":\"syn\",\"optional\":true,\"req\":\"^2.0.72\"},{\"kind\":\"build\",\"name\":\"vcpkg\",\"optional\":true,\"req\":\"^0.2.15\"}],\"features\":{\"buildtime_bindgen\":[\"bindgen\",\"pkg-config\",\"vcpkg\"],\"bundled\":[\"cc\",\"bundled_bindings\"],\"bundled-sqlcipher\":[\"bundled\"],\"bundled-sqlcipher-vendored-openssl\":[\"bundled-sqlcipher\",\"openssl-sys/vendored\"],\"bundled-windows\":[\"cc\",\"bundled_bindings\"],\"bundled_bindings\":[],\"default\":[\"min_sqlite_version_3_14_0\"],\"in_gecko\":[],\"loadable_extension\":[\"prettyplease\",\"quote\",\"syn\"],\"min_sqlite_version_3_14_0\":[\"pkg-config\",\"vcpkg\"],\"preupdate_hook\":[\"buildtime_bindgen\"],\"session\":[\"preupdate_hook\",\"buildtime_bindgen\"],\"sqlcipher\":[],\"unlock_notify\":[],\"wasm32-wasi-vfs\":[],\"with-asan\":[]}}", "linux-keyutils_0.2.4": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bitflags\",\"req\":\"^2.4\"},{\"default_features\":false,\"features\":[\"std\",\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.4.11\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.132\"},{\"kind\":\"dev\",\"name\":\"zeroize\",\"req\":\"^1.5.7\"}],\"features\":{\"default\":[],\"std\":[\"bitflags/std\"]}}", + "linux-raw-sys_0.11.0": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.100\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"auxvec\":[],\"bootparam\":[],\"btrfs\":[],\"default\":[\"std\",\"general\",\"errno\"],\"elf\":[],\"elf_uapi\":[],\"errno\":[],\"general\":[],\"if_arp\":[],\"if_ether\":[],\"if_packet\":[],\"image\":[],\"io_uring\":[],\"ioctl\":[],\"landlock\":[],\"loop_device\":[],\"mempolicy\":[],\"net\":[],\"netlink\":[],\"no_std\":[],\"prctl\":[],\"ptrace\":[],\"rustc-dep-of-std\":[\"core\",\"no_std\"],\"std\":[],\"system\":[],\"xdp\":[]}}", "linux-raw-sys_0.4.15": "{\"dependencies\":[{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.49\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.100\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"bootparam\":[],\"btrfs\":[],\"default\":[\"std\",\"general\",\"errno\"],\"elf\":[],\"elf_uapi\":[],\"errno\":[],\"general\":[],\"if_arp\":[],\"if_ether\":[],\"if_packet\":[],\"io_uring\":[],\"ioctl\":[],\"landlock\":[],\"loop_device\":[],\"mempolicy\":[],\"net\":[],\"netlink\":[],\"no_std\":[],\"prctl\":[],\"ptrace\":[],\"rustc-dep-of-std\":[\"core\",\"compiler_builtins\",\"no_std\"],\"std\":[],\"system\":[],\"xdp\":[]}}", - "linux-raw-sys_0.9.4": "{\"dependencies\":[{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.49\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.100\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"bootparam\":[],\"btrfs\":[],\"default\":[\"std\",\"general\",\"errno\"],\"elf\":[],\"elf_uapi\":[],\"errno\":[],\"general\":[],\"if_arp\":[],\"if_ether\":[],\"if_packet\":[],\"image\":[],\"io_uring\":[],\"ioctl\":[],\"landlock\":[],\"loop_device\":[],\"mempolicy\":[],\"net\":[],\"netlink\":[],\"no_std\":[],\"prctl\":[],\"ptrace\":[],\"rustc-dep-of-std\":[\"core\",\"compiler_builtins\",\"no_std\"],\"std\":[],\"system\":[],\"xdp\":[]}}", - "litemap_0.8.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"use-std\"],\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"features\":[\"validation\"],\"kind\":\"dev\",\"name\":\"rkyv\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.110\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.110\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"yoke\",\"optional\":true,\"req\":\"^0.8.0\"}],\"features\":{\"alloc\":[],\"databake\":[\"dep:databake\"],\"default\":[\"alloc\"],\"serde\":[\"dep:serde\",\"alloc\"],\"testing\":[\"alloc\"],\"yoke\":[\"dep:yoke\"]}}", + "litemap_0.8.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"use-std\"],\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"features\":[\"validation\"],\"kind\":\"dev\",\"name\":\"rkyv\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde_core\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"yoke\",\"optional\":true,\"req\":\"^0.8.0\"}],\"features\":{\"alloc\":[],\"databake\":[\"dep:databake\"],\"default\":[\"alloc\"],\"serde\":[\"dep:serde_core\",\"alloc\"],\"testing\":[\"alloc\"],\"yoke\":[\"dep:yoke\"]}}", "local-waker_0.1.4": "{\"dependencies\":[],\"features\":{}}", - "lock_api_0.4.13": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"autocfg\",\"req\":\"^1.1.0\"},{\"name\":\"owning_ref\",\"optional\":true,\"req\":\"^0.4.1\"},{\"default_features\":false,\"name\":\"scopeguard\",\"req\":\"^1.1.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.126\"}],\"features\":{\"arc_lock\":[],\"atomic_usize\":[],\"default\":[\"atomic_usize\"],\"nightly\":[]}}", + "lock_api_0.4.14": "{\"dependencies\":[{\"name\":\"owning_ref\",\"optional\":true,\"req\":\"^0.4.1\"},{\"default_features\":false,\"name\":\"scopeguard\",\"req\":\"^1.1.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.126\"}],\"features\":{\"arc_lock\":[],\"atomic_usize\":[],\"default\":[\"atomic_usize\"],\"nightly\":[]}}", "log_0.4.29": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.63\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"sval\",\"optional\":true,\"req\":\"^2.16\"},{\"kind\":\"dev\",\"name\":\"sval\",\"req\":\"^2.16\"},{\"kind\":\"dev\",\"name\":\"sval_derive\",\"req\":\"^2.16\"},{\"default_features\":false,\"name\":\"sval_ref\",\"optional\":true,\"req\":\"^2.16\"},{\"default_features\":false,\"features\":[\"inline-i128\"],\"name\":\"value-bag\",\"optional\":true,\"req\":\"^1.12\"},{\"features\":[\"test\"],\"kind\":\"dev\",\"name\":\"value-bag\",\"req\":\"^1.12\"}],\"features\":{\"kv\":[],\"kv_serde\":[\"kv_std\",\"value-bag/serde\",\"serde\"],\"kv_std\":[\"std\",\"kv\",\"value-bag/error\"],\"kv_sval\":[\"kv\",\"value-bag/sval\",\"sval\",\"sval_ref\"],\"kv_unstable\":[\"kv\",\"value-bag\"],\"kv_unstable_serde\":[\"kv_serde\",\"kv_unstable_std\"],\"kv_unstable_std\":[\"kv_std\",\"kv_unstable\"],\"kv_unstable_sval\":[\"kv_sval\",\"kv_unstable\"],\"max_level_debug\":[],\"max_level_error\":[],\"max_level_info\":[],\"max_level_off\":[],\"max_level_trace\":[],\"max_level_warn\":[],\"release_max_level_debug\":[],\"release_max_level_error\":[],\"release_max_level_info\":[],\"release_max_level_off\":[],\"release_max_level_trace\":[],\"release_max_level_warn\":[],\"serde\":[\"serde_core\"],\"std\":[]}}", "logos-derive_0.12.1": "{\"dependencies\":[{\"name\":\"beef\",\"req\":\"^0.5.0\"},{\"name\":\"fnv\",\"req\":\"^1.0.6\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^0.6.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.9\"},{\"name\":\"quote\",\"req\":\"^1.0.3\"},{\"name\":\"regex-syntax\",\"req\":\"^0.6\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^1.0.17\"}],\"features\":{}}", "logos_0.12.1": "{\"dependencies\":[{\"name\":\"logos-derive\",\"optional\":true,\"req\":\"^0.12.1\"}],\"features\":{\"default\":[\"export_derive\",\"std\"],\"export_derive\":[\"logos-derive\"],\"std\":[]}}", @@ -901,22 +929,24 @@ "lru_0.12.5": "{\"dependencies\":[{\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15\"},{\"kind\":\"dev\",\"name\":\"scoped_threadpool\",\"req\":\"0.1.*\"},{\"kind\":\"dev\",\"name\":\"stats_alloc\",\"req\":\"0.1.*\"}],\"features\":{\"default\":[\"hashbrown\"],\"nightly\":[\"hashbrown\",\"hashbrown/nightly\"]}}", "lru_0.16.3": "{\"dependencies\":[{\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.16.0\"},{\"kind\":\"dev\",\"name\":\"scoped_threadpool\",\"req\":\"0.1.*\"},{\"kind\":\"dev\",\"name\":\"stats_alloc\",\"req\":\"0.1.*\"}],\"features\":{\"default\":[\"hashbrown\"],\"nightly\":[\"hashbrown\",\"hashbrown/nightly\"]}}", "lsp-types_0.94.1": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^1.0.1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.34\"},{\"name\":\"serde_json\",\"req\":\"^1.0.50\"},{\"name\":\"serde_repr\",\"req\":\"^0.1\"},{\"features\":[\"serde\"],\"name\":\"url\",\"req\":\"^2.0.0\"}],\"features\":{\"default\":[],\"proposed\":[]}}", + "lzma-rs_0.3.0": "{\"dependencies\":[{\"name\":\"byteorder\",\"req\":\"^1.4.3\"},{\"name\":\"crc\",\"req\":\"^3.0.0\"},{\"name\":\"env_logger\",\"optional\":true,\"req\":\"^0.9.0\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.17\"},{\"kind\":\"dev\",\"name\":\"rust-lzma\",\"req\":\"^0.5\"}],\"features\":{\"enable_logging\":[\"env_logger\",\"log\"],\"raw_decoder\":[],\"stream\":[]}}", + "lzma-sys_0.1.20": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.34\"},{\"name\":\"libc\",\"req\":\"^0.2.51\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.14\"}],\"features\":{\"static\":[]}}", "maplit_1.0.2": "{\"dependencies\":[],\"features\":{}}", "matchers_0.2.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"syntax\",\"dfa-build\",\"dfa-search\"],\"name\":\"regex-automata\",\"req\":\"^0.4\"}],\"features\":{\"unicode\":[\"regex-automata/unicode\"]}}", "matchit_0.8.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"actix-router\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.4\"},{\"kind\":\"dev\",\"name\":\"gonzales\",\"req\":\"^0.0.3-beta\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^0.14\"},{\"kind\":\"dev\",\"name\":\"path-tree\",\"req\":\"^0.2.2\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.5.4\"},{\"kind\":\"dev\",\"name\":\"route-recognizer\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"routefinder\",\"req\":\"^0.5.2\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"make\",\"util\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.4\"}],\"features\":{\"__test_helpers\":[],\"default\":[]}}", "matchit_0.9.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"actix-router\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"gonzales\",\"req\":\"^0.0.3-beta\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"features\":[\"http1\",\"server\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1\"},{\"features\":[\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"path-tree\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"route-recognizer\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"routefinder\",\"req\":\"^0.5\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"make\",\"util\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"kind\":\"dev\",\"name\":\"wayfind\",\"req\":\"^0.8\"}],\"features\":{\"__test_helpers\":[],\"default\":[]}}", "md-5_0.10.6": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.2.2\"},{\"name\":\"md5-asm\",\"optional\":true,\"req\":\"^0.5\",\"target\":\"cfg(any(target_arch = \\\"x86\\\", target_arch = \\\"x86_64\\\"))\"}],\"features\":{\"asm\":[\"md5-asm\"],\"default\":[\"std\"],\"force-soft\":[],\"loongarch64_asm\":[],\"oid\":[\"digest/oid\"],\"std\":[\"digest/std\"]}}", "md5_0.8.0": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", - "memchr_2.7.5": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.20\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"libc\":[],\"logging\":[\"dep:log\"],\"rustc-dep-of-std\":[\"core\"],\"std\":[\"alloc\"],\"use_std\":[\"std\"]}}", + "memchr_2.7.6": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.20\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"libc\":[],\"logging\":[\"dep:log\"],\"rustc-dep-of-std\":[\"core\"],\"std\":[\"alloc\"],\"use_std\":[\"std\"]}}", "memoffset_0.6.5": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"autocfg\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"}],\"features\":{\"default\":[],\"unstable_const\":[]}}", "memoffset_0.9.1": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"autocfg\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"}],\"features\":{\"default\":[],\"unstable_const\":[],\"unstable_offset_of\":[]}}", "mime_0.3.17": "{\"dependencies\":[],\"features\":{}}", "mime_guess_2.0.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"name\":\"mime\",\"req\":\"^0.3\"},{\"name\":\"unicase\",\"req\":\"^2.4.0\"},{\"kind\":\"build\",\"name\":\"unicase\",\"req\":\"^2.4.0\"}],\"features\":{\"default\":[\"rev-mappings\"],\"rev-mappings\":[]}}", "minimal-lexical_0.2.1": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"compact\":[],\"default\":[\"std\"],\"lint\":[],\"nightly\":[],\"std\":[]}}", "miniz_oxide_0.8.9": "{\"dependencies\":[{\"default_features\":false,\"name\":\"adler2\",\"req\":\"^2.0\"},{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"simd-adler32\",\"optional\":true,\"req\":\"^0.3.3\"}],\"features\":{\"block-boundary\":[],\"default\":[\"with-alloc\"],\"rustc-dep-of-std\":[\"core\",\"alloc\",\"adler2/rustc-dep-of-std\"],\"simd\":[\"simd-adler32\"],\"std\":[],\"with-alloc\":[]}}", - "mio_1.0.4": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.9.3\"},{\"name\":\"libc\",\"req\":\"^0.2.159\",\"target\":\"cfg(target_os = \\\"hermit\\\")\"},{\"name\":\"libc\",\"req\":\"^0.2.159\",\"target\":\"cfg(target_os = \\\"wasi\\\")\"},{\"name\":\"libc\",\"req\":\"^0.2.159\",\"target\":\"cfg(unix)\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"wasi\",\"req\":\"^0.11.0\",\"target\":\"cfg(target_os = \\\"wasi\\\")\"},{\"features\":[\"Wdk_Foundation\",\"Wdk_Storage_FileSystem\",\"Wdk_System_IO\",\"Win32_Foundation\",\"Win32_Networking_WinSock\",\"Win32_Storage_FileSystem\",\"Win32_System_IO\",\"Win32_System_WindowsProgramming\"],\"name\":\"windows-sys\",\"req\":\"^0.59\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"log\"],\"net\":[],\"os-ext\":[\"os-poll\",\"windows-sys/Win32_System_Pipes\",\"windows-sys/Win32_Security\"],\"os-poll\":[]}}", - "moka_0.12.12": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"actix-rt\",\"req\":\"^2.8\"},{\"kind\":\"dev\",\"name\":\"ahash\",\"req\":\"^0.8.3\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.19\"},{\"name\":\"async-lock\",\"optional\":true,\"req\":\"^3.3\"},{\"name\":\"crossbeam-channel\",\"req\":\"^0.5.15\"},{\"name\":\"crossbeam-epoch\",\"req\":\"^0.9.18\"},{\"name\":\"crossbeam-utils\",\"req\":\"^0.8.21\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10.0\"},{\"name\":\"equivalent\",\"req\":\"^1.0\"},{\"name\":\"event-listener\",\"optional\":true,\"req\":\"^5.3\"},{\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.17\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(moka_loom)\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.7\"},{\"name\":\"parking_lot\",\"req\":\"^0.12\"},{\"name\":\"portable-atomic\",\"req\":\"^1.6\"},{\"name\":\"quanta\",\"optional\":true,\"req\":\"^0.12.2\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"default_features\":false,\"features\":[\"rustls-tls\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12\"},{\"name\":\"smallvec\",\"req\":\"^1.8\"},{\"name\":\"tagptr\",\"req\":\"^0.2\"},{\"features\":[\"fs\",\"io-util\",\"macros\",\"rt-multi-thread\",\"sync\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.19\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\",\"target\":\"cfg(trybuild)\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"req\":\"^1.1\"}],\"features\":{\"atomic64\":[],\"default\":[],\"future\":[\"async-lock\",\"event-listener\",\"futures-util\"],\"logging\":[\"log\"],\"quanta\":[\"dep:quanta\"],\"sync\":[],\"unstable-debug-counters\":[\"future\"]}}", - "moxcms_0.7.5": "{\"dependencies\":[{\"name\":\"num-traits\",\"req\":\"^0.2\"},{\"name\":\"pxfm\",\"req\":\"^0.1.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"}],\"features\":{\"avx\":[],\"avx512\":[],\"default\":[\"avx\",\"sse\",\"neon\"],\"neon\":[],\"options\":[],\"sse\":[]}}", + "mio_1.1.1": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"name\":\"libc\",\"req\":\"^0.2.178\",\"target\":\"cfg(target_os = \\\"hermit\\\")\"},{\"name\":\"libc\",\"req\":\"^0.2.178\",\"target\":\"cfg(target_os = \\\"wasi\\\")\"},{\"name\":\"libc\",\"req\":\"^0.2.178\",\"target\":\"cfg(unix)\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"name\":\"wasi\",\"req\":\"^0.11.0\",\"target\":\"cfg(target_os = \\\"wasi\\\")\"},{\"features\":[\"Wdk_Foundation\",\"Wdk_Storage_FileSystem\",\"Wdk_System_IO\",\"Win32_Foundation\",\"Win32_Networking_WinSock\",\"Win32_Storage_FileSystem\",\"Win32_Security\",\"Win32_System_IO\",\"Win32_System_WindowsProgramming\"],\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"log\"],\"net\":[],\"os-ext\":[\"os-poll\",\"windows-sys/Win32_System_Pipes\",\"windows-sys/Win32_Security\"],\"os-poll\":[]}}", + "moka_0.12.13": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"actix-rt\",\"req\":\"^2.8\"},{\"kind\":\"dev\",\"name\":\"ahash\",\"req\":\"^0.8.3\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.19\"},{\"name\":\"async-lock\",\"optional\":true,\"req\":\"^3.3\"},{\"name\":\"crossbeam-channel\",\"req\":\"^0.5.15\"},{\"name\":\"crossbeam-epoch\",\"req\":\"^0.9.18\"},{\"name\":\"crossbeam-utils\",\"req\":\"^0.8.21\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10.0\"},{\"name\":\"equivalent\",\"req\":\"^1.0\"},{\"name\":\"event-listener\",\"optional\":true,\"req\":\"^5.3\"},{\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.17\"},{\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(moka_loom)\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.7\"},{\"name\":\"parking_lot\",\"req\":\"^0.12\"},{\"name\":\"portable-atomic\",\"req\":\"^1.6\"},{\"name\":\"quanta\",\"optional\":true,\"req\":\"^0.12.2\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"default_features\":false,\"features\":[\"rustls-tls\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12\"},{\"name\":\"smallvec\",\"req\":\"^1.8\"},{\"name\":\"tagptr\",\"req\":\"^0.2\"},{\"features\":[\"fs\",\"io-util\",\"macros\",\"rt-multi-thread\",\"sync\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.19\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\",\"target\":\"cfg(trybuild)\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"req\":\"^1.1\"}],\"features\":{\"atomic64\":[],\"default\":[],\"future\":[\"async-lock\",\"event-listener\",\"futures-util\"],\"logging\":[\"log\"],\"quanta\":[\"dep:quanta\"],\"sync\":[],\"unstable-debug-counters\":[\"future\"]}}", + "moxcms_0.7.11": "{\"dependencies\":[{\"name\":\"num-traits\",\"req\":\"^0.2\"},{\"name\":\"pxfm\",\"req\":\"^0.1.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"}],\"features\":{\"avx\":[],\"avx512\":[],\"default\":[\"avx\",\"sse\",\"neon\"],\"neon\":[],\"options\":[],\"sse\":[]}}", "multimap_0.10.1": "{\"dependencies\":[{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"serde_impl\"],\"serde_impl\":[\"serde\"]}}", "native-tls_0.2.14": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"name\":\"log\",\"req\":\"^0.4.5\",\"target\":\"cfg(not(any(target_os = \\\"windows\\\", target_vendor = \\\"apple\\\")))\"},{\"name\":\"openssl\",\"req\":\"^0.10.69\",\"target\":\"cfg(not(any(target_os = \\\"windows\\\", target_vendor = \\\"apple\\\")))\"},{\"name\":\"openssl-probe\",\"req\":\"^0.1\",\"target\":\"cfg(not(any(target_os = \\\"windows\\\", target_vendor = \\\"apple\\\")))\"},{\"name\":\"openssl-sys\",\"req\":\"^0.9.81\",\"target\":\"cfg(not(any(target_os = \\\"windows\\\", target_vendor = \\\"apple\\\")))\"},{\"name\":\"schannel\",\"req\":\"^0.1.17\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"security-framework\",\"req\":\"^2.0.0\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"name\":\"security-framework-sys\",\"req\":\"^2.0.0\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"name\":\"tempfile\",\"req\":\"^3.1.0\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.0\"},{\"kind\":\"dev\",\"name\":\"test-cert-gen\",\"req\":\"^0.9\"}],\"features\":{\"alpn\":[\"security-framework/alpn\"],\"vendored\":[\"openssl/vendored\"]}}", "ndk-context_0.1.1": "{\"dependencies\":[],\"features\":{}}", @@ -928,13 +958,13 @@ "nom_7.1.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2.3\"},{\"default_features\":false,\"name\":\"minimal-lexical\",\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.0.0\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"docsrs\":[],\"std\":[\"alloc\",\"memchr/std\",\"minimal-lexical/std\"]}}", "nom_8.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2.3\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"=1.0.0\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"docsrs\":[],\"std\":[\"alloc\",\"memchr/std\"]}}", "normalize-line-endings_0.3.0": "{\"dependencies\":[],\"features\":{}}", - "notify-types_2.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.34.0\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.24.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.89\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.39\"},{\"name\":\"web-time\",\"optional\":true,\"req\":\"^1.1.0\"}],\"features\":{\"serialization-compat-6\":[]}}", + "notify-types_2.1.0": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.34.0\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.26.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.89\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.39\"},{\"name\":\"web-time\",\"optional\":true,\"req\":\"^1.1.0\"}],\"features\":{\"serde\":[\"dep:serde\",\"bitflags/serde\"],\"serialization-compat-6\":[]}}", "notify_8.2.0": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.7.0\",\"target\":\"cfg(target_os=\\\"macos\\\")\"},{\"name\":\"crossbeam-channel\",\"optional\":true,\"req\":\"^0.5.0\"},{\"name\":\"flume\",\"optional\":true,\"req\":\"^0.11.1\"},{\"name\":\"fsevent-sys\",\"optional\":true,\"req\":\"^4.0.0\",\"target\":\"cfg(target_os=\\\"macos\\\")\"},{\"default_features\":false,\"name\":\"inotify\",\"req\":\"^0.11.0\",\"target\":\"cfg(any(target_os=\\\"linux\\\", target_os=\\\"android\\\"))\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.34.0\"},{\"name\":\"kqueue\",\"req\":\"^1.1.1\",\"target\":\"cfg(any(target_os=\\\"freebsd\\\", target_os=\\\"openbsd\\\", target_os = \\\"netbsd\\\", target_os = \\\"dragonflybsd\\\", target_os = \\\"ios\\\"))\"},{\"name\":\"kqueue\",\"optional\":true,\"req\":\"^1.1.1\",\"target\":\"cfg(target_os=\\\"macos\\\")\"},{\"name\":\"libc\",\"req\":\"^0.2.4\"},{\"name\":\"log\",\"req\":\"^0.4.17\"},{\"features\":[\"os-ext\"],\"name\":\"mio\",\"req\":\"^1.0\",\"target\":\"cfg(any(target_os=\\\"freebsd\\\", target_os=\\\"openbsd\\\", target_os = \\\"netbsd\\\", target_os = \\\"dragonflybsd\\\", target_os = \\\"ios\\\"))\"},{\"features\":[\"os-ext\"],\"name\":\"mio\",\"req\":\"^1.0\",\"target\":\"cfg(any(target_os=\\\"linux\\\", target_os=\\\"android\\\"))\"},{\"features\":[\"os-ext\"],\"name\":\"mio\",\"optional\":true,\"req\":\"^1.0\",\"target\":\"cfg(target_os=\\\"macos\\\")\"},{\"kind\":\"dev\",\"name\":\"nix\",\"req\":\"^0.29.0\"},{\"name\":\"notify-types\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.39\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.10.0\"},{\"kind\":\"dev\",\"name\":\"trash\",\"req\":\"^5.2.2\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"walkdir\",\"req\":\"^2.4.0\"},{\"features\":[\"Win32_System_Threading\",\"Win32_Foundation\",\"Win32_Storage_FileSystem\",\"Win32_Security\",\"Win32_System_WindowsProgramming\",\"Win32_System_IO\"],\"name\":\"windows-sys\",\"req\":\"^0.60.1\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"macos_fsevent\"],\"macos_fsevent\":[\"fsevent-sys\"],\"macos_kqueue\":[\"kqueue\",\"mio\"],\"serde\":[\"notify-types/serde\"],\"serialization-compat-6\":[\"notify-types/serialization-compat-6\"]}}", - "nu-ansi-term_0.50.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.3\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.152\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.94\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Console\",\"Win32_Storage_FileSystem\",\"Win32_Security\"],\"name\":\"windows\",\"package\":\"windows-sys\",\"req\":\"^0.52.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"derive_serde_style\":[\"serde\"],\"gnu_legacy\":[]}}", + "nu-ansi-term_0.50.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.3\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.152\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.94\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Console\",\"Win32_Storage_FileSystem\",\"Win32_Security\"],\"name\":\"windows\",\"package\":\"windows-sys\",\"req\":\">=0.59, <=0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"std\"],\"derive_serde_style\":[\"serde\"],\"gnu_legacy\":[],\"std\":[]}}", "num-bigint-dig_0.8.6": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.1.0\"},{\"default_features\":false,\"features\":[\"spin_no_std\"],\"name\":\"lazy_static\",\"req\":\"^1.2.0\"},{\"name\":\"libm\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-integer\",\"req\":\"^0.1.39\"},{\"default_features\":false,\"name\":\"num-iter\",\"req\":\"^0.1.37\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-traits\",\"req\":\"^0.2.4\"},{\"default_features\":false,\"name\":\"rand\",\"optional\":true,\"req\":\"^0.8.3\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand_chacha\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"rand_isaac\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"smallvec\",\"req\":\"^1.10.0\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.5\"}],\"features\":{\"default\":[\"std\",\"u64_digit\"],\"fuzz\":[\"arbitrary\",\"smallvec/arbitrary\"],\"i128\":[],\"nightly\":[],\"prime\":[\"rand/std_rng\"],\"std\":[\"num-integer/std\",\"num-traits/std\",\"smallvec/write\",\"rand/std\",\"serde/std\"],\"u64_digit\":[]}}", "num-bigint_0.4.6": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-integer\",\"req\":\"^0.1.46\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-traits\",\"req\":\"^0.2.18\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"name\":\"rand\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"arbitrary\":[\"dep:arbitrary\"],\"default\":[\"std\"],\"quickcheck\":[\"dep:quickcheck\"],\"rand\":[\"dep:rand\"],\"serde\":[\"dep:serde\"],\"std\":[\"num-integer/std\",\"num-traits/std\"]}}", "num-complex_0.4.6": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bytecheck\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"bytemuck\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-traits\",\"req\":\"^0.2.18\"},{\"default_features\":false,\"name\":\"rand\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"bytecheck\":[\"dep:bytecheck\"],\"bytemuck\":[\"dep:bytemuck\"],\"default\":[\"std\"],\"libm\":[\"num-traits/libm\"],\"rand\":[\"dep:rand\"],\"rkyv\":[\"dep:rkyv\"],\"serde\":[\"dep:serde\"],\"std\":[\"num-traits/std\"]}}", - "num-conv_0.1.0": "{\"dependencies\":[],\"features\":{}}", + "num-conv_0.2.0": "{\"dependencies\":[],\"features\":{}}", "num-integer_0.1.46": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-traits\",\"req\":\"^0.2.11\"}],\"features\":{\"default\":[\"std\"],\"i128\":[],\"std\":[\"num-traits/std\"]}}", "num-iter_0.1.45": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"autocfg\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-integer\",\"req\":\"^0.1.46\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-traits\",\"req\":\"^0.2.11\"}],\"features\":{\"default\":[\"std\"],\"i128\":[],\"std\":[\"num-integer/std\",\"num-traits/std\"]}}", "num-rational_0.4.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"num-bigint\",\"optional\":true,\"req\":\"^0.4.0\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-integer\",\"req\":\"^0.1.42\"},{\"default_features\":false,\"features\":[\"i128\"],\"name\":\"num-traits\",\"req\":\"^0.2.18\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.0\"}],\"features\":{\"default\":[\"num-bigint\",\"std\"],\"num-bigint\":[\"dep:num-bigint\"],\"num-bigint-std\":[\"num-bigint/std\"],\"serde\":[\"dep:serde\"],\"std\":[\"num-bigint?/std\",\"num-integer/std\",\"num-traits/std\"]}}", @@ -943,21 +973,31 @@ "num_cpus_1.17.0": "{\"dependencies\":[{\"name\":\"hermit-abi\",\"req\":\"^0.5.0\",\"target\":\"cfg(target_os = \\\"hermit\\\")\"},{\"name\":\"libc\",\"req\":\"^0.2.26\",\"target\":\"cfg(not(windows))\"}],\"features\":{}}", "num_threads_0.1.7": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.107\",\"target\":\"cfg(any(target_os = \\\"macos\\\", target_os = \\\"ios\\\", target_os = \\\"freebsd\\\"))\"}],\"features\":{}}", "oauth2_5.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.13\"},{\"name\":\"base64\",\"req\":\">=0.21, <0.23\"},{\"default_features\":false,\"features\":[\"clock\",\"serde\",\"std\",\"wasmbind\"],\"name\":\"chrono\",\"req\":\"^0.4.31\"},{\"name\":\"curl\",\"optional\":true,\"req\":\"^0.4.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"js\"],\"name\":\"getrandom\",\"req\":\"^0.2\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"hmac\",\"req\":\"^0.12\"},{\"name\":\"http\",\"req\":\"^1.0\"},{\"name\":\"rand\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"serde_path_to_error\",\"req\":\"^0.1.2\"},{\"name\":\"sha2\",\"req\":\"^0.10\"},{\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"name\":\"ureq\",\"optional\":true,\"req\":\"^2\"},{\"features\":[\"serde\"],\"name\":\"url\",\"req\":\"^2.1\"},{\"features\":[\"v4\"],\"kind\":\"dev\",\"name\":\"uuid\",\"req\":\"^1.10\"}],\"features\":{\"default\":[\"reqwest\",\"rustls-tls\"],\"native-tls\":[\"reqwest/native-tls\"],\"pkce-plain\":[],\"reqwest-blocking\":[\"reqwest/blocking\"],\"rustls-tls\":[\"reqwest/rustls-tls\"],\"timing-resistant-secret-traits\":[]}}", - "objc2-app-kit_0.3.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"CKContainer\",\"CKRecord\",\"CKShare\",\"CKShareMetadata\"],\"name\":\"objc2-cloud-kit\",\"optional\":true,\"req\":\"^0.3.1\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"NSAttributeDescription\",\"NSEntityDescription\",\"NSFetchRequest\",\"NSManagedObjectContext\",\"NSManagedObjectModel\",\"NSPersistentStoreRequest\",\"NSPropertyDescription\"],\"name\":\"objc2-core-data\",\"optional\":true,\"req\":\"^0.3.1\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CFCGTypes\",\"objc2\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.1\"},{\"default_features\":false,\"features\":[\"CGColor\",\"CGColorSpace\",\"CGContext\",\"CGEventTypes\",\"CGFont\",\"CGImage\",\"CGPath\",\"objc2\"],\"name\":\"objc2-core-graphics\",\"optional\":true,\"req\":\"^0.3.1\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CIColor\",\"CIContext\",\"CIFilter\",\"CIImage\"],\"name\":\"objc2-core-image\",\"optional\":true,\"req\":\"^0.3.1\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"features\":[\"CADisplayLink\",\"CALayer\",\"CAMediaTimingFunction\"],\"name\":\"objc2-quartz-core\",\"optional\":true,\"req\":\"^0.3.1\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"UTType\"],\"name\":\"objc2-uniform-type-identifiers\",\"optional\":true,\"req\":\"^0.3.1\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"}],\"features\":{\"AppKitDefines\":[],\"AppKitErrors\":[],\"NSATSTypesetter\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/objc2-core-foundation\"],\"NSAccessibility\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSAccessibilityColor\":[\"objc2-foundation/NSString\"],\"NSAccessibilityConstants\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAccessibilityCustomAction\":[\"objc2-foundation/NSString\"],\"NSAccessibilityCustomRotor\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSAccessibilityElement\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSAccessibilityProtocols\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSActionCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAdaptiveImageGlyph\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAffineTransform\":[\"objc2-foundation/NSAffineTransform\"],\"NSAlert\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"NSAlignmentFeedbackFilter\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/objc2-core-foundation\"],\"NSAnimation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSAnimationContext\":[\"objc2-foundation/NSDate\"],\"NSAppearance\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAppleScriptExtensions\":[\"objc2-foundation/NSAppleScript\",\"objc2-foundation/NSAttributedString\"],\"NSApplication\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSException\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUserActivity\"],\"NSApplicationScripting\":[\"objc2-foundation/NSArray\"],\"NSArrayController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"NSAttributedString\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSBezierPath\":[\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSBitmapImageRep\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSBox\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSBrowser\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSBrowserCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSButton\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSButtonCell\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSButtonTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSCIImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCachedImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCandidateListTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSCell\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSFormatter\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSClickGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSClipView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewCompositionalLayout\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewFlowLayout\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewGridLayout\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewLayout\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewTransitionLayout\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSColor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorList\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSColorPanel\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorPicker\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorPickerTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSColorPicking\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorSampler\":[],\"NSColorSpace\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSColorWell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSComboBox\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSComboBoxCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSComboButton\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSControl\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSFormatter\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSController\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSCursor\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCustomImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCustomTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDataAsset\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDatePicker\":[\"objc2-foundation/NSCalendar\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSTimeZone\",\"objc2-foundation/objc2-core-foundation\"],\"NSDatePickerCell\":[\"bitflags\",\"objc2-foundation/NSCalendar\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTimeZone\"],\"NSDictionaryController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDiffableDataSource\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDirection\":[\"bitflags\"],\"NSDockTile\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSDocument\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFilePresenter\",\"objc2-foundation/NSFileVersion\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUndoManager\"],\"NSDocumentController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSDocumentScripting\":[\"objc2-foundation/NSScriptCommand\",\"objc2-foundation/NSScriptObjectSpecifiers\",\"objc2-foundation/NSScriptStandardSuiteCommands\",\"objc2-foundation/NSString\"],\"NSDragging\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSDraggingItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSDraggingSession\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSDrawer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSEPSImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSErrors\":[\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSString\"],\"NSEvent\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSFilePromiseProvider\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSFilePromiseReceiver\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSFileWrapperExtensions\":[\"objc2-foundation/NSFileWrapper\"],\"NSFont\":[\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSFontAssetRequest\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSProgress\"],\"NSFontCollection\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSFontDescriptor\":[\"bitflags\",\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSFontManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSFontPanel\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSForm\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSFormCell\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSGlyphGenerator\":[\"objc2-foundation/NSAttributedString\"],\"NSGlyphInfo\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSGradient\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSGraphics\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGraphicsContext\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGridView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/objc2-core-foundation\"],\"NSGroupTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSHapticFeedback\":[],\"NSHelpManager\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSImage\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSImageCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSImageRep\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSImageView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSInputManager\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSInputServer\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSInterfaceStyle\":[\"objc2-foundation/NSString\"],\"NSItemProvider\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSKeyValueBinding\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSLayoutAnchor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSLayoutConstraint\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSLayoutGuide\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSLayoutManager\":[\"bitflags\",\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSLevelIndicator\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSLevelIndicatorCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSMagnificationGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSMatrix\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSMediaLibraryBrowserController\":[\"bitflags\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/objc2-core-foundation\"],\"NSMenu\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSZone\",\"objc2-foundation/objc2-core-foundation\"],\"NSMenuItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSMenuItemBadge\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSMenuItemCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSMenuToolbarItem\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSMovie\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSNib\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSNibConnector\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSNibControlConnector\":[\"objc2-foundation/NSObject\"],\"NSNibDeclarations\":[],\"NSNibLoading\":[],\"NSNibOutletConnector\":[\"objc2-foundation/NSObject\"],\"NSObjectController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"NSOpenGL\":[],\"NSOpenGLLayer\":[],\"NSOpenGLView\":[],\"NSOpenPanel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSOutlineView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPDFImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPDFInfo\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPDFPanel\":[\"bitflags\",\"objc2-foundation/NSString\"],\"NSPICTImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPageController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPageLayout\":[\"objc2-foundation/NSArray\"],\"NSPanGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPanel\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSParagraphStyle\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPasteboard\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPasteboardItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSPathCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPathComponentCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPathControl\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPathControlItem\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPersistentDocument\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFilePresenter\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPickerTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPopUpButton\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPopUpButtonCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPopover\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPopoverTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPredicateEditor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPredicateEditorRowTemplate\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSComparisonPredicate\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSPressGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"NSPressureConfiguration\":[],\"NSPreviewRepresentingActivityItem\":[\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\"],\"NSPrintInfo\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPrintOperation\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPrintPanel\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSPrinter\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSProgressIndicator\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSProgress\",\"objc2-foundation/objc2-core-foundation\"],\"NSResponder\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUndoManager\"],\"NSRotationGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSRuleEditor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSRulerMarker\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSRulerView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSRunningApplication\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSavePanel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSScreen\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrollView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScroller\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrubber\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrubberItemView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrubberLayout\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/objc2-core-foundation\"],\"NSSearchField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSearchFieldCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSearchToolbarItem\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSecureTextField\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSegmentedCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSegmentedControl\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSShadow\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSSharingCollaborationModeRestriction\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSharingService\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSSharingServicePickerToolbarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSharingServicePickerTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSlider\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSliderAccessory\":[\"objc2-foundation/NSObject\"],\"NSSliderCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSliderTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSound\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSpeechRecognizer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSSpeechSynthesizer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSpellChecker\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSOrthography\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\",\"objc2-foundation/objc2-core-foundation\"],\"NSSpellProtocol\":[],\"NSSplitView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSplitViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSplitViewItem\":[\"objc2-foundation/NSObject\"],\"NSStackView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSStatusBar\":[],\"NSStatusBarButton\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSStatusItem\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSStepper\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSStepperCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSStepperTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSFormatter\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSStoryboard\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSString\"],\"NSStoryboardSegue\":[\"objc2-foundation/NSString\"],\"NSStringDrawing\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSwitch\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTabView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTabViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTabViewItem\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableCellView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableColumn\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"NSTableHeaderCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableHeaderView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableRowView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSEnumerator\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableViewDiffableDataSource\":[],\"NSTableViewRowAction\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSText\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextAlternatives\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextAttachment\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextAttachmentCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextCheckingClient\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextCheckingController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\"],\"NSTextContainer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextContent\":[\"objc2-foundation/NSString\"],\"NSTextContentManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextElement\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\"],\"NSTextField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextFieldCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextFinder\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextInputClient\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextInputContext\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"NSTextInsertionIndicator\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextLayoutFragment\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\"],\"NSTextLayoutManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"NSTextLineFragment\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextList\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextListElement\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSTextRange\":[\"objc2-foundation/NSObjCRuntime\"],\"NSTextSelection\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextSelectionNavigation\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSTextStorage\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextStorageScripting\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\"],\"NSTextTable\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOrthography\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUndoManager\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextViewportLayoutController\":[],\"NSTintConfiguration\":[\"objc2-foundation/NSObject\"],\"NSTitlebarAccessoryViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTokenField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTokenFieldCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSToolbar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSToolbarItem\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSToolbarItemGroup\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTouch\":[\"bitflags\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTouchBar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTrackingArea\":[\"bitflags\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTrackingSeparatorToolbarItem\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTreeController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"NSTreeNode\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSSortDescriptor\"],\"NSTypesetter\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSUserActivity\":[\"objc2-foundation/NSString\",\"objc2-foundation/NSUserActivity\"],\"NSUserDefaultsController\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUserDefaults\"],\"NSUserInterfaceCompression\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSUserInterfaceItemIdentification\":[\"objc2-foundation/NSString\"],\"NSUserInterfaceItemSearching\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSUserInterfaceLayout\":[],\"NSUserInterfaceValidation\":[],\"NSView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSViewController\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSExtensionContext\",\"objc2-foundation/NSExtensionRequestHandling\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSVisualEffectView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSWindow\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUndoManager\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSWindowController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSWindowRestoration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"NSWindowScripting\":[\"objc2-foundation/NSScriptCommand\",\"objc2-foundation/NSScriptStandardSuiteCommands\"],\"NSWindowTab\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\"],\"NSWindowTabGroup\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSWorkspace\":[\"bitflags\",\"objc2-foundation/NSAppleEventDescriptor\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFileManager\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSWritingToolsCoordinator\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSUUID\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSWritingToolsCoordinatorAnimationParameters\":[],\"NSWritingToolsCoordinatorContext\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSUUID\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"AppKitDefines\",\"AppKitErrors\",\"NSATSTypesetter\",\"NSAccessibility\",\"NSAccessibilityColor\",\"NSAccessibilityConstants\",\"NSAccessibilityCustomAction\",\"NSAccessibilityCustomRotor\",\"NSAccessibilityElement\",\"NSAccessibilityProtocols\",\"NSActionCell\",\"NSAdaptiveImageGlyph\",\"NSAffineTransform\",\"NSAlert\",\"NSAlignmentFeedbackFilter\",\"NSAnimation\",\"NSAnimationContext\",\"NSAppearance\",\"NSAppleScriptExtensions\",\"NSApplication\",\"NSApplicationScripting\",\"NSArrayController\",\"NSAttributedString\",\"NSBezierPath\",\"NSBitmapImageRep\",\"NSBox\",\"NSBrowser\",\"NSBrowserCell\",\"NSButton\",\"NSButtonCell\",\"NSButtonTouchBarItem\",\"NSCIImageRep\",\"NSCachedImageRep\",\"NSCandidateListTouchBarItem\",\"NSCell\",\"NSClickGestureRecognizer\",\"NSClipView\",\"NSCollectionView\",\"NSCollectionViewCompositionalLayout\",\"NSCollectionViewFlowLayout\",\"NSCollectionViewGridLayout\",\"NSCollectionViewLayout\",\"NSCollectionViewTransitionLayout\",\"NSColor\",\"NSColorList\",\"NSColorPanel\",\"NSColorPicker\",\"NSColorPickerTouchBarItem\",\"NSColorPicking\",\"NSColorSampler\",\"NSColorSpace\",\"NSColorWell\",\"NSComboBox\",\"NSComboBoxCell\",\"NSComboButton\",\"NSControl\",\"NSController\",\"NSCursor\",\"NSCustomImageRep\",\"NSCustomTouchBarItem\",\"NSDataAsset\",\"NSDatePicker\",\"NSDatePickerCell\",\"NSDictionaryController\",\"NSDiffableDataSource\",\"NSDirection\",\"NSDockTile\",\"NSDocument\",\"NSDocumentController\",\"NSDocumentScripting\",\"NSDragging\",\"NSDraggingItem\",\"NSDraggingSession\",\"NSDrawer\",\"NSEPSImageRep\",\"NSErrors\",\"NSEvent\",\"NSFilePromiseProvider\",\"NSFilePromiseReceiver\",\"NSFileWrapperExtensions\",\"NSFont\",\"NSFontAssetRequest\",\"NSFontCollection\",\"NSFontDescriptor\",\"NSFontManager\",\"NSFontPanel\",\"NSForm\",\"NSFormCell\",\"NSGestureRecognizer\",\"NSGlyphGenerator\",\"NSGlyphInfo\",\"NSGradient\",\"NSGraphics\",\"NSGraphicsContext\",\"NSGridView\",\"NSGroupTouchBarItem\",\"NSHapticFeedback\",\"NSHelpManager\",\"NSImage\",\"NSImageCell\",\"NSImageRep\",\"NSImageView\",\"NSInputManager\",\"NSInputServer\",\"NSInterfaceStyle\",\"NSItemProvider\",\"NSKeyValueBinding\",\"NSLayoutAnchor\",\"NSLayoutConstraint\",\"NSLayoutGuide\",\"NSLayoutManager\",\"NSLevelIndicator\",\"NSLevelIndicatorCell\",\"NSMagnificationGestureRecognizer\",\"NSMatrix\",\"NSMediaLibraryBrowserController\",\"NSMenu\",\"NSMenuItem\",\"NSMenuItemBadge\",\"NSMenuItemCell\",\"NSMenuToolbarItem\",\"NSMovie\",\"NSNib\",\"NSNibConnector\",\"NSNibControlConnector\",\"NSNibDeclarations\",\"NSNibLoading\",\"NSNibOutletConnector\",\"NSObjectController\",\"NSOpenGL\",\"NSOpenGLLayer\",\"NSOpenGLView\",\"NSOpenPanel\",\"NSOutlineView\",\"NSPDFImageRep\",\"NSPDFInfo\",\"NSPDFPanel\",\"NSPICTImageRep\",\"NSPageController\",\"NSPageLayout\",\"NSPanGestureRecognizer\",\"NSPanel\",\"NSParagraphStyle\",\"NSPasteboard\",\"NSPasteboardItem\",\"NSPathCell\",\"NSPathComponentCell\",\"NSPathControl\",\"NSPathControlItem\",\"NSPersistentDocument\",\"NSPickerTouchBarItem\",\"NSPopUpButton\",\"NSPopUpButtonCell\",\"NSPopover\",\"NSPopoverTouchBarItem\",\"NSPredicateEditor\",\"NSPredicateEditorRowTemplate\",\"NSPressGestureRecognizer\",\"NSPressureConfiguration\",\"NSPreviewRepresentingActivityItem\",\"NSPrintInfo\",\"NSPrintOperation\",\"NSPrintPanel\",\"NSPrinter\",\"NSProgressIndicator\",\"NSResponder\",\"NSRotationGestureRecognizer\",\"NSRuleEditor\",\"NSRulerMarker\",\"NSRulerView\",\"NSRunningApplication\",\"NSSavePanel\",\"NSScreen\",\"NSScrollView\",\"NSScroller\",\"NSScrubber\",\"NSScrubberItemView\",\"NSScrubberLayout\",\"NSSearchField\",\"NSSearchFieldCell\",\"NSSearchToolbarItem\",\"NSSecureTextField\",\"NSSegmentedCell\",\"NSSegmentedControl\",\"NSShadow\",\"NSSharingCollaborationModeRestriction\",\"NSSharingService\",\"NSSharingServicePickerToolbarItem\",\"NSSharingServicePickerTouchBarItem\",\"NSSlider\",\"NSSliderAccessory\",\"NSSliderCell\",\"NSSliderTouchBarItem\",\"NSSound\",\"NSSpeechRecognizer\",\"NSSpeechSynthesizer\",\"NSSpellChecker\",\"NSSpellProtocol\",\"NSSplitView\",\"NSSplitViewController\",\"NSSplitViewItem\",\"NSStackView\",\"NSStatusBar\",\"NSStatusBarButton\",\"NSStatusItem\",\"NSStepper\",\"NSStepperCell\",\"NSStepperTouchBarItem\",\"NSStoryboard\",\"NSStoryboardSegue\",\"NSStringDrawing\",\"NSSwitch\",\"NSTabView\",\"NSTabViewController\",\"NSTabViewItem\",\"NSTableCellView\",\"NSTableColumn\",\"NSTableHeaderCell\",\"NSTableHeaderView\",\"NSTableRowView\",\"NSTableView\",\"NSTableViewDiffableDataSource\",\"NSTableViewRowAction\",\"NSText\",\"NSTextAlternatives\",\"NSTextAttachment\",\"NSTextAttachmentCell\",\"NSTextCheckingClient\",\"NSTextCheckingController\",\"NSTextContainer\",\"NSTextContent\",\"NSTextContentManager\",\"NSTextElement\",\"NSTextField\",\"NSTextFieldCell\",\"NSTextFinder\",\"NSTextInputClient\",\"NSTextInputContext\",\"NSTextInsertionIndicator\",\"NSTextLayoutFragment\",\"NSTextLayoutManager\",\"NSTextLineFragment\",\"NSTextList\",\"NSTextListElement\",\"NSTextRange\",\"NSTextSelection\",\"NSTextSelectionNavigation\",\"NSTextStorage\",\"NSTextStorageScripting\",\"NSTextTable\",\"NSTextView\",\"NSTextViewportLayoutController\",\"NSTintConfiguration\",\"NSTitlebarAccessoryViewController\",\"NSTokenField\",\"NSTokenFieldCell\",\"NSToolbar\",\"NSToolbarItem\",\"NSToolbarItemGroup\",\"NSTouch\",\"NSTouchBar\",\"NSTouchBarItem\",\"NSTrackingArea\",\"NSTrackingSeparatorToolbarItem\",\"NSTreeController\",\"NSTreeNode\",\"NSTypesetter\",\"NSUserActivity\",\"NSUserDefaultsController\",\"NSUserInterfaceCompression\",\"NSUserInterfaceItemIdentification\",\"NSUserInterfaceItemSearching\",\"NSUserInterfaceLayout\",\"NSUserInterfaceValidation\",\"NSView\",\"NSViewController\",\"NSVisualEffectView\",\"NSWindow\",\"NSWindowController\",\"NSWindowRestoration\",\"NSWindowScripting\",\"NSWindowTab\",\"NSWindowTabGroup\",\"NSWorkspace\",\"NSWritingToolsCoordinator\",\"NSWritingToolsCoordinatorAnimationParameters\",\"NSWritingToolsCoordinatorContext\",\"bitflags\",\"block2\",\"libc\",\"objc2-cloud-kit\",\"objc2-core-data\",\"objc2-core-foundation\",\"objc2-core-graphics\",\"objc2-core-image\",\"objc2-quartz-core\"],\"gnustep-1-7\":[\"objc2/gnustep-1-7\",\"block2?/gnustep-1-7\",\"objc2-foundation/gnustep-1-7\",\"objc2-core-data?/gnustep-1-7\",\"objc2-quartz-core?/gnustep-1-7\"],\"gnustep-1-8\":[\"gnustep-1-7\",\"objc2/gnustep-1-8\",\"block2?/gnustep-1-8\",\"objc2-foundation/gnustep-1-8\",\"objc2-core-data?/gnustep-1-8\",\"objc2-quartz-core?/gnustep-1-8\"],\"gnustep-1-9\":[\"gnustep-1-8\",\"objc2/gnustep-1-9\",\"block2?/gnustep-1-9\",\"objc2-foundation/gnustep-1-9\",\"objc2-core-data?/gnustep-1-9\",\"objc2-quartz-core?/gnustep-1-9\"],\"gnustep-2-0\":[\"gnustep-1-9\",\"objc2/gnustep-2-0\",\"block2?/gnustep-2-0\",\"objc2-foundation/gnustep-2-0\",\"objc2-core-data?/gnustep-2-0\",\"objc2-quartz-core?/gnustep-2-0\"],\"gnustep-2-1\":[\"gnustep-2-0\",\"objc2/gnustep-2-1\",\"block2?/gnustep-2-1\",\"objc2-foundation/gnustep-2-1\",\"objc2-core-data?/gnustep-2-1\",\"objc2-quartz-core?/gnustep-2-1\"],\"libc\":[\"dep:libc\"],\"objc2-cloud-kit\":[\"dep:objc2-cloud-kit\"],\"objc2-core-data\":[\"dep:objc2-core-data\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-core-graphics\":[\"dep:objc2-core-graphics\"],\"objc2-core-image\":[\"dep:objc2-core-image\"],\"objc2-quartz-core\":[\"dep:objc2-quartz-core\"],\"objc2-uniform-type-identifiers\":[\"dep:objc2-uniform-type-identifiers\"],\"std\":[\"alloc\"]}}", - "objc2-core-foundation_0.3.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"dispatch2\",\"optional\":true,\"req\":\">=0.3.0, <0.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"CFArray\":[],\"CFAttributedString\":[],\"CFAvailability\":[],\"CFBag\":[],\"CFBase\":[],\"CFBinaryHeap\":[],\"CFBitVector\":[],\"CFBundle\":[],\"CFByteOrder\":[],\"CFCGTypes\":[],\"CFCalendar\":[\"bitflags\"],\"CFCharacterSet\":[],\"CFData\":[\"bitflags\"],\"CFDate\":[\"bitflags\"],\"CFDateFormatter\":[\"bitflags\"],\"CFDictionary\":[],\"CFError\":[],\"CFFileDescriptor\":[],\"CFFileSecurity\":[\"bitflags\"],\"CFLocale\":[],\"CFMachPort\":[],\"CFMessagePort\":[],\"CFNotificationCenter\":[],\"CFNumber\":[],\"CFNumberFormatter\":[\"bitflags\"],\"CFPlugIn\":[],\"CFPlugInCOM\":[],\"CFPreferences\":[],\"CFPropertyList\":[\"bitflags\"],\"CFRunLoop\":[\"bitflags\"],\"CFSet\":[],\"CFSocket\":[\"bitflags\"],\"CFStream\":[\"bitflags\"],\"CFString\":[\"bitflags\"],\"CFStringEncodingExt\":[],\"CFStringTokenizer\":[\"bitflags\"],\"CFTimeZone\":[],\"CFTree\":[],\"CFURL\":[\"bitflags\"],\"CFURLAccess\":[],\"CFURLEnumerator\":[\"bitflags\"],\"CFUUID\":[],\"CFUserNotification\":[],\"CFUtilities\":[],\"CFXMLNode\":[],\"CFXMLParser\":[\"bitflags\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CFArray\",\"CFAttributedString\",\"CFAvailability\",\"CFBag\",\"CFBase\",\"CFBinaryHeap\",\"CFBitVector\",\"CFBundle\",\"CFByteOrder\",\"CFCGTypes\",\"CFCalendar\",\"CFCharacterSet\",\"CFData\",\"CFDate\",\"CFDateFormatter\",\"CFDictionary\",\"CFError\",\"CFFileDescriptor\",\"CFFileSecurity\",\"CFLocale\",\"CFMachPort\",\"CFMessagePort\",\"CFNotificationCenter\",\"CFNumber\",\"CFNumberFormatter\",\"CFPlugIn\",\"CFPlugInCOM\",\"CFPreferences\",\"CFPropertyList\",\"CFRunLoop\",\"CFSet\",\"CFSocket\",\"CFStream\",\"CFString\",\"CFStringEncodingExt\",\"CFStringTokenizer\",\"CFTimeZone\",\"CFTree\",\"CFURL\",\"CFURLAccess\",\"CFURLEnumerator\",\"CFUUID\",\"CFUserNotification\",\"CFUtilities\",\"CFXMLNode\",\"CFXMLParser\",\"bitflags\",\"block2\",\"dispatch2\",\"libc\",\"objc2\"],\"dispatch2\":[\"dep:dispatch2\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\",\"dispatch2?/objc2\"],\"std\":[\"alloc\"],\"unstable-coerce-pointee\":[]}}", - "objc2-core-graphics_0.3.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"dispatch2\",\"optional\":true,\"req\":\">=0.3.0, <0.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"name\":\"objc2-core-foundation\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"features\":[\"IOSurfaceRef\"],\"name\":\"objc2-io-surface\",\"optional\":true,\"req\":\"^0.3.1\",\"target\":\"cfg(not(target_os = \\\"watchos\\\"))\"},{\"default_features\":false,\"features\":[\"MTLDevice\"],\"name\":\"objc2-metal\",\"optional\":true,\"req\":\"^0.3.1\",\"target\":\"cfg(not(target_os = \\\"watchos\\\"))\"}],\"features\":{\"CGAffineTransform\":[\"objc2-core-foundation/CFCGTypes\"],\"CGBase\":[],\"CGBitmapContext\":[],\"CGColor\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGColorConversionInfo\":[\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFError\"],\"CGColorSpace\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\"],\"CGContext\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGConvertColorDataWithFormat\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGDataConsumer\":[\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFURL\"],\"CGDataProvider\":[\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFURL\"],\"CGDirectDisplay\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGDirectDisplayMetal\":[],\"CGDirectPalette\":[],\"CGDisplayConfiguration\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGDisplayFade\":[],\"CGDisplayStream\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFRunLoop\"],\"CGEXRToneMappingGamma\":[],\"CGError\":[],\"CGEvent\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFMachPort\"],\"CGEventSource\":[\"objc2-core-foundation/CFDate\"],\"CGEventTypes\":[\"bitflags\"],\"CGFont\":[\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFDictionary\"],\"CGFunction\":[\"objc2-core-foundation/CFCGTypes\"],\"CGGeometry\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGGradient\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\"],\"CGITUToneMapping\":[],\"CGImage\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\"],\"CGLayer\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGPDFArray\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFContentStream\":[\"objc2-core-foundation/CFArray\"],\"CGPDFContext\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFURL\"],\"CGPDFDictionary\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFDocument\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFURL\"],\"CGPDFObject\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFOperatorTable\":[],\"CGPDFPage\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFScanner\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFStream\":[\"objc2-core-foundation/CFData\"],\"CGPDFString\":[\"objc2-core-foundation/CFDate\"],\"CGPSConverter\":[\"objc2-core-foundation/CFDictionary\"],\"CGPath\":[\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\"],\"CGPattern\":[\"objc2-core-foundation/CFCGTypes\"],\"CGRemoteOperation\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDate\",\"objc2-core-foundation/CFMachPort\"],\"CGSession\":[\"objc2-core-foundation/CFDictionary\"],\"CGShading\":[\"objc2-core-foundation/CFCGTypes\"],\"CGToneMapping\":[],\"CGWindow\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\"],\"CGWindowLevel\":[],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CGAffineTransform\",\"CGBase\",\"CGBitmapContext\",\"CGColor\",\"CGColorConversionInfo\",\"CGColorSpace\",\"CGContext\",\"CGConvertColorDataWithFormat\",\"CGDataConsumer\",\"CGDataProvider\",\"CGDirectDisplay\",\"CGDirectDisplayMetal\",\"CGDirectPalette\",\"CGDisplayConfiguration\",\"CGDisplayFade\",\"CGDisplayStream\",\"CGEXRToneMappingGamma\",\"CGError\",\"CGEvent\",\"CGEventSource\",\"CGEventTypes\",\"CGFont\",\"CGFunction\",\"CGGeometry\",\"CGGradient\",\"CGITUToneMapping\",\"CGImage\",\"CGLayer\",\"CGPDFArray\",\"CGPDFContentStream\",\"CGPDFContext\",\"CGPDFDictionary\",\"CGPDFDocument\",\"CGPDFObject\",\"CGPDFOperatorTable\",\"CGPDFPage\",\"CGPDFScanner\",\"CGPDFStream\",\"CGPDFString\",\"CGPSConverter\",\"CGPath\",\"CGPattern\",\"CGRemoteOperation\",\"CGSession\",\"CGShading\",\"CGToneMapping\",\"CGWindow\",\"CGWindowLevel\",\"bitflags\",\"block2\",\"dispatch2\",\"libc\",\"objc2\",\"objc2-metal\"],\"dispatch2\":[\"dep:dispatch2\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\",\"dispatch2?/objc2\",\"objc2-core-foundation/objc2\",\"objc2-io-surface?/objc2\"],\"objc2-io-surface\":[\"dep:objc2-io-surface\"],\"objc2-metal\":[\"dep:objc2-metal\"],\"std\":[\"alloc\"]}}", + "objc2-app-kit_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CKContainer\",\"CKRecord\",\"CKShare\",\"CKShareMetadata\"],\"name\":\"objc2-cloud-kit\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"NSAttributeDescription\",\"NSEntityDescription\",\"NSFetchRequest\",\"NSManagedObjectContext\",\"NSManagedObjectModel\",\"NSPersistentStoreRequest\",\"NSPropertyDescription\"],\"name\":\"objc2-core-data\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CFCGTypes\",\"CFDate\",\"objc2\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGColor\",\"CGColorSpace\",\"CGContext\",\"CGDirectDisplay\",\"CGEventTypes\",\"CGFont\",\"CGImage\",\"CGPath\",\"objc2\"],\"name\":\"objc2-core-graphics\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CIColor\",\"CIContext\",\"CIFilter\",\"CIImage\"],\"name\":\"objc2-core-image\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CTFont\",\"CTFontCollection\",\"CTFontDescriptor\",\"CTGlyphInfo\",\"objc2\"],\"name\":\"objc2-core-text\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CVBase\",\"objc2\"],\"name\":\"objc2-core-video\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGLTypes\"],\"name\":\"objc2-open-gl\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"CADisplayLink\",\"CALayer\",\"CAMediaTiming\",\"CAMediaTimingFunction\",\"CAOpenGLLayer\"],\"name\":\"objc2-quartz-core\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"default_features\":false,\"features\":[\"UTType\"],\"name\":\"objc2-uniform-type-identifiers\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"}],\"features\":{\"AppKitDefines\":[],\"AppKitErrors\":[],\"NSATSTypesetter\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/objc2-core-foundation\"],\"NSAccessibility\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSAccessibilityColor\":[\"objc2-foundation/NSString\"],\"NSAccessibilityConstants\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAccessibilityCustomAction\":[\"objc2-foundation/NSString\"],\"NSAccessibilityCustomRotor\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSAccessibilityElement\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSAccessibilityProtocols\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSActionCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAdaptiveImageGlyph\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAffineTransform\":[\"objc2-foundation/NSAffineTransform\"],\"NSAlert\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"NSAlignmentFeedbackFilter\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/objc2-core-foundation\"],\"NSAnimation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSAnimationContext\":[\"objc2-foundation/NSDate\"],\"NSAppearance\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAppleScriptExtensions\":[\"objc2-foundation/NSAppleScript\",\"objc2-foundation/NSAttributedString\"],\"NSApplication\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSException\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUserActivity\"],\"NSApplicationScripting\":[\"objc2-foundation/NSArray\"],\"NSArrayController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"NSAttributedString\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSBackgroundExtensionView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSBezierPath\":[\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSBitmapImageRep\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSBox\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSBrowser\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSBrowserCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSButton\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSButtonCell\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSButtonTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSCIImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCachedImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCandidateListTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSCell\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSFormatter\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSClickGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSClipView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewCompositionalLayout\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewFlowLayout\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewGridLayout\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewLayout\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSCollectionViewTransitionLayout\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSColor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorList\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSColorPanel\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorPicker\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorPickerTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSColorPicking\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSColorSampler\":[],\"NSColorSpace\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSColorWell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSComboBox\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSComboBoxCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSComboButton\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSControl\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSFormatter\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSController\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSCursor\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCustomImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSCustomTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDataAsset\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDatePicker\":[\"objc2-foundation/NSCalendar\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSTimeZone\",\"objc2-foundation/objc2-core-foundation\"],\"NSDatePickerCell\":[\"bitflags\",\"objc2-foundation/NSCalendar\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTimeZone\"],\"NSDictionaryController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDiffableDataSource\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDirection\":[\"bitflags\"],\"NSDockTile\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSDocument\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFilePresenter\",\"objc2-foundation/NSFileVersion\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUndoManager\"],\"NSDocumentController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSDocumentScripting\":[\"objc2-foundation/NSScriptCommand\",\"objc2-foundation/NSScriptObjectSpecifiers\",\"objc2-foundation/NSScriptStandardSuiteCommands\",\"objc2-foundation/NSString\"],\"NSDragging\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSDraggingItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSDraggingSession\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSDrawer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSEPSImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSErrors\":[\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSString\"],\"NSEvent\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSFilePromiseProvider\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSFilePromiseReceiver\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSFileWrapperExtensions\":[\"objc2-foundation/NSFileWrapper\"],\"NSFont\":[\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSFontAssetRequest\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSProgress\"],\"NSFontCollection\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSFontDescriptor\":[\"bitflags\",\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSFontManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSFontPanel\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSForm\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSFormCell\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGlassEffectView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSGlyphGenerator\":[\"objc2-foundation/NSAttributedString\"],\"NSGlyphInfo\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSGradient\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSGraphics\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGraphicsContext\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSGridView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/objc2-core-foundation\"],\"NSGroupTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSHapticFeedback\":[],\"NSHelpManager\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSImage\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSImageCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSImageRep\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSImageView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSInputManager\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSInputServer\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSInterfaceStyle\":[\"objc2-foundation/NSString\"],\"NSItemBadge\":[\"objc2-foundation/NSString\"],\"NSItemProvider\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSKeyValueBinding\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSLayoutAnchor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSLayoutConstraint\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSLayoutGuide\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSLayoutManager\":[\"bitflags\",\"objc2-foundation/NSAffineTransform\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSLevelIndicator\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSLevelIndicatorCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSMagnificationGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSMatrix\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSMediaLibraryBrowserController\":[\"bitflags\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/objc2-core-foundation\"],\"NSMenu\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSZone\",\"objc2-foundation/objc2-core-foundation\"],\"NSMenuItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSMenuItemBadge\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSMenuItemCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSMenuToolbarItem\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSMovie\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSNib\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSNibConnector\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSNibControlConnector\":[\"objc2-foundation/NSObject\"],\"NSNibDeclarations\":[],\"NSNibLoading\":[],\"NSNibOutletConnector\":[\"objc2-foundation/NSObject\"],\"NSObjectController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"NSOpenGL\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSLock\",\"objc2-foundation/NSObject\"],\"NSOpenGLLayer\":[\"objc2-foundation/NSObject\"],\"NSOpenGLView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSOpenPanel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSOutlineView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPDFImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPDFInfo\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPDFPanel\":[\"bitflags\",\"objc2-foundation/NSString\"],\"NSPICTImageRep\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPageController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPageLayout\":[\"objc2-foundation/NSArray\"],\"NSPanGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPanel\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSParagraphStyle\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPasteboard\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPasteboardItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSPathCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPathComponentCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPathControl\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSPathControlItem\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPersistentDocument\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFilePresenter\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPickerTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPopUpButton\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPopUpButtonCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPopover\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPopoverTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPredicateEditor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSPredicateEditorRowTemplate\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSComparisonPredicate\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSPressGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"NSPressureConfiguration\":[],\"NSPreviewRepresentingActivityItem\":[\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\"],\"NSPrintInfo\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPrintOperation\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSPrintPanel\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSPrinter\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSProgressIndicator\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSProgress\",\"objc2-foundation/objc2-core-foundation\"],\"NSResponder\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUndoManager\"],\"NSRotationGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSRuleEditor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSRulerMarker\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSRulerView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSRunningApplication\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSavePanel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSScreen\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrollView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScroller\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrubber\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrubberItemView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSScrubberLayout\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/objc2-core-foundation\"],\"NSSearchField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSearchFieldCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSearchToolbarItem\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSecureTextField\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSegmentedCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSegmentedControl\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSShadow\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSSharingCollaborationModeRestriction\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSharingService\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSSharingServicePickerToolbarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSharingServicePickerTouchBarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSlider\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSliderAccessory\":[\"objc2-foundation/NSObject\"],\"NSSliderCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSliderTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSSound\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSpeechRecognizer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSSpeechSynthesizer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSSpellChecker\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSOrthography\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\",\"objc2-foundation/objc2-core-foundation\"],\"NSSpellProtocol\":[],\"NSSplitView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSplitViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSplitViewItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"NSSplitViewItemAccessoryViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSStackView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSStatusBar\":[],\"NSStatusBarButton\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSStatusItem\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSStepper\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSStepperCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSStepperTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSFormatter\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSStoryboard\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSString\"],\"NSStoryboardSegue\":[\"objc2-foundation/NSString\"],\"NSStringDrawing\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSSwitch\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTabView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTabViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTabViewItem\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableCellView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableColumn\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"NSTableHeaderCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableHeaderView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableRowView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSEnumerator\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/objc2-core-foundation\"],\"NSTableViewDiffableDataSource\":[],\"NSTableViewRowAction\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSText\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextAlternatives\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextAttachment\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextAttachmentCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextCheckingClient\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextCheckingController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\"],\"NSTextContainer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextContent\":[\"objc2-foundation/NSString\"],\"NSTextContentManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextElement\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\"],\"NSTextField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextFieldCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextFinder\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextInputClient\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextInputContext\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"NSTextInsertionIndicator\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextLayoutFragment\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\"],\"NSTextLayoutManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"NSTextLineFragment\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextList\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextListElement\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSTextRange\":[\"objc2-foundation/NSObjCRuntime\"],\"NSTextSelection\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextSelectionNavigation\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSTextStorage\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextStorageScripting\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\"],\"NSTextTable\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOrthography\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTextCheckingResult\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUndoManager\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSTextViewportLayoutController\":[],\"NSTintConfiguration\":[\"objc2-foundation/NSObject\"],\"NSTintProminence\":[],\"NSTitlebarAccessoryViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTokenField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSTokenFieldCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSToolbar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSToolbarItem\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSToolbarItemGroup\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTouch\":[\"bitflags\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTouchBar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSTouchBarItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTrackingArea\":[\"bitflags\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSTrackingSeparatorToolbarItem\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTreeController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"NSTreeNode\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSSortDescriptor\"],\"NSTypesetter\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSUserActivity\":[\"objc2-foundation/NSString\",\"objc2-foundation/NSUserActivity\"],\"NSUserDefaultsController\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUserDefaults\"],\"NSUserInterfaceCompression\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSUserInterfaceItemIdentification\":[\"objc2-foundation/NSString\"],\"NSUserInterfaceItemSearching\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSUserInterfaceLayout\":[],\"NSUserInterfaceValidation\":[],\"NSView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSViewController\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSExtensionContext\",\"objc2-foundation/NSExtensionRequestHandling\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/objc2-core-foundation\"],\"NSViewLayoutRegion\":[\"objc2-foundation/NSGeometry\",\"objc2-foundation/objc2-core-foundation\"],\"NSVisualEffectView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSObject\",\"objc2-foundation/objc2-core-foundation\"],\"NSWindow\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUndoManager\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSWindowController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSWindowRestoration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"NSWindowScripting\":[\"objc2-foundation/NSScriptCommand\",\"objc2-foundation/NSScriptStandardSuiteCommands\"],\"NSWindowTab\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\"],\"NSWindowTabGroup\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSWorkspace\":[\"bitflags\",\"objc2-foundation/NSAppleEventDescriptor\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFileManager\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSWritingToolsCoordinator\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSGeometry\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSUUID\",\"objc2-foundation/NSValue\",\"objc2-foundation/objc2-core-foundation\"],\"NSWritingToolsCoordinatorAnimationParameters\":[],\"NSWritingToolsCoordinatorContext\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSUUID\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"AppKitDefines\",\"AppKitErrors\",\"NSATSTypesetter\",\"NSAccessibility\",\"NSAccessibilityColor\",\"NSAccessibilityConstants\",\"NSAccessibilityCustomAction\",\"NSAccessibilityCustomRotor\",\"NSAccessibilityElement\",\"NSAccessibilityProtocols\",\"NSActionCell\",\"NSAdaptiveImageGlyph\",\"NSAffineTransform\",\"NSAlert\",\"NSAlignmentFeedbackFilter\",\"NSAnimation\",\"NSAnimationContext\",\"NSAppearance\",\"NSAppleScriptExtensions\",\"NSApplication\",\"NSApplicationScripting\",\"NSArrayController\",\"NSAttributedString\",\"NSBackgroundExtensionView\",\"NSBezierPath\",\"NSBitmapImageRep\",\"NSBox\",\"NSBrowser\",\"NSBrowserCell\",\"NSButton\",\"NSButtonCell\",\"NSButtonTouchBarItem\",\"NSCIImageRep\",\"NSCachedImageRep\",\"NSCandidateListTouchBarItem\",\"NSCell\",\"NSClickGestureRecognizer\",\"NSClipView\",\"NSCollectionView\",\"NSCollectionViewCompositionalLayout\",\"NSCollectionViewFlowLayout\",\"NSCollectionViewGridLayout\",\"NSCollectionViewLayout\",\"NSCollectionViewTransitionLayout\",\"NSColor\",\"NSColorList\",\"NSColorPanel\",\"NSColorPicker\",\"NSColorPickerTouchBarItem\",\"NSColorPicking\",\"NSColorSampler\",\"NSColorSpace\",\"NSColorWell\",\"NSComboBox\",\"NSComboBoxCell\",\"NSComboButton\",\"NSControl\",\"NSController\",\"NSCursor\",\"NSCustomImageRep\",\"NSCustomTouchBarItem\",\"NSDataAsset\",\"NSDatePicker\",\"NSDatePickerCell\",\"NSDictionaryController\",\"NSDiffableDataSource\",\"NSDirection\",\"NSDockTile\",\"NSDocument\",\"NSDocumentController\",\"NSDocumentScripting\",\"NSDragging\",\"NSDraggingItem\",\"NSDraggingSession\",\"NSDrawer\",\"NSEPSImageRep\",\"NSErrors\",\"NSEvent\",\"NSFilePromiseProvider\",\"NSFilePromiseReceiver\",\"NSFileWrapperExtensions\",\"NSFont\",\"NSFontAssetRequest\",\"NSFontCollection\",\"NSFontDescriptor\",\"NSFontManager\",\"NSFontPanel\",\"NSForm\",\"NSFormCell\",\"NSGestureRecognizer\",\"NSGlassEffectView\",\"NSGlyphGenerator\",\"NSGlyphInfo\",\"NSGradient\",\"NSGraphics\",\"NSGraphicsContext\",\"NSGridView\",\"NSGroupTouchBarItem\",\"NSHapticFeedback\",\"NSHelpManager\",\"NSImage\",\"NSImageCell\",\"NSImageRep\",\"NSImageView\",\"NSInputManager\",\"NSInputServer\",\"NSInterfaceStyle\",\"NSItemBadge\",\"NSItemProvider\",\"NSKeyValueBinding\",\"NSLayoutAnchor\",\"NSLayoutConstraint\",\"NSLayoutGuide\",\"NSLayoutManager\",\"NSLevelIndicator\",\"NSLevelIndicatorCell\",\"NSMagnificationGestureRecognizer\",\"NSMatrix\",\"NSMediaLibraryBrowserController\",\"NSMenu\",\"NSMenuItem\",\"NSMenuItemBadge\",\"NSMenuItemCell\",\"NSMenuToolbarItem\",\"NSMovie\",\"NSNib\",\"NSNibConnector\",\"NSNibControlConnector\",\"NSNibDeclarations\",\"NSNibLoading\",\"NSNibOutletConnector\",\"NSObjectController\",\"NSOpenGL\",\"NSOpenGLLayer\",\"NSOpenGLView\",\"NSOpenPanel\",\"NSOutlineView\",\"NSPDFImageRep\",\"NSPDFInfo\",\"NSPDFPanel\",\"NSPICTImageRep\",\"NSPageController\",\"NSPageLayout\",\"NSPanGestureRecognizer\",\"NSPanel\",\"NSParagraphStyle\",\"NSPasteboard\",\"NSPasteboardItem\",\"NSPathCell\",\"NSPathComponentCell\",\"NSPathControl\",\"NSPathControlItem\",\"NSPersistentDocument\",\"NSPickerTouchBarItem\",\"NSPopUpButton\",\"NSPopUpButtonCell\",\"NSPopover\",\"NSPopoverTouchBarItem\",\"NSPredicateEditor\",\"NSPredicateEditorRowTemplate\",\"NSPressGestureRecognizer\",\"NSPressureConfiguration\",\"NSPreviewRepresentingActivityItem\",\"NSPrintInfo\",\"NSPrintOperation\",\"NSPrintPanel\",\"NSPrinter\",\"NSProgressIndicator\",\"NSResponder\",\"NSRotationGestureRecognizer\",\"NSRuleEditor\",\"NSRulerMarker\",\"NSRulerView\",\"NSRunningApplication\",\"NSSavePanel\",\"NSScreen\",\"NSScrollView\",\"NSScroller\",\"NSScrubber\",\"NSScrubberItemView\",\"NSScrubberLayout\",\"NSSearchField\",\"NSSearchFieldCell\",\"NSSearchToolbarItem\",\"NSSecureTextField\",\"NSSegmentedCell\",\"NSSegmentedControl\",\"NSShadow\",\"NSSharingCollaborationModeRestriction\",\"NSSharingService\",\"NSSharingServicePickerToolbarItem\",\"NSSharingServicePickerTouchBarItem\",\"NSSlider\",\"NSSliderAccessory\",\"NSSliderCell\",\"NSSliderTouchBarItem\",\"NSSound\",\"NSSpeechRecognizer\",\"NSSpeechSynthesizer\",\"NSSpellChecker\",\"NSSpellProtocol\",\"NSSplitView\",\"NSSplitViewController\",\"NSSplitViewItem\",\"NSSplitViewItemAccessoryViewController\",\"NSStackView\",\"NSStatusBar\",\"NSStatusBarButton\",\"NSStatusItem\",\"NSStepper\",\"NSStepperCell\",\"NSStepperTouchBarItem\",\"NSStoryboard\",\"NSStoryboardSegue\",\"NSStringDrawing\",\"NSSwitch\",\"NSTabView\",\"NSTabViewController\",\"NSTabViewItem\",\"NSTableCellView\",\"NSTableColumn\",\"NSTableHeaderCell\",\"NSTableHeaderView\",\"NSTableRowView\",\"NSTableView\",\"NSTableViewDiffableDataSource\",\"NSTableViewRowAction\",\"NSText\",\"NSTextAlternatives\",\"NSTextAttachment\",\"NSTextAttachmentCell\",\"NSTextCheckingClient\",\"NSTextCheckingController\",\"NSTextContainer\",\"NSTextContent\",\"NSTextContentManager\",\"NSTextElement\",\"NSTextField\",\"NSTextFieldCell\",\"NSTextFinder\",\"NSTextInputClient\",\"NSTextInputContext\",\"NSTextInsertionIndicator\",\"NSTextLayoutFragment\",\"NSTextLayoutManager\",\"NSTextLineFragment\",\"NSTextList\",\"NSTextListElement\",\"NSTextRange\",\"NSTextSelection\",\"NSTextSelectionNavigation\",\"NSTextStorage\",\"NSTextStorageScripting\",\"NSTextTable\",\"NSTextView\",\"NSTextViewportLayoutController\",\"NSTintConfiguration\",\"NSTintProminence\",\"NSTitlebarAccessoryViewController\",\"NSTokenField\",\"NSTokenFieldCell\",\"NSToolbar\",\"NSToolbarItem\",\"NSToolbarItemGroup\",\"NSTouch\",\"NSTouchBar\",\"NSTouchBarItem\",\"NSTrackingArea\",\"NSTrackingSeparatorToolbarItem\",\"NSTreeController\",\"NSTreeNode\",\"NSTypesetter\",\"NSUserActivity\",\"NSUserDefaultsController\",\"NSUserInterfaceCompression\",\"NSUserInterfaceItemIdentification\",\"NSUserInterfaceItemSearching\",\"NSUserInterfaceLayout\",\"NSUserInterfaceValidation\",\"NSView\",\"NSViewController\",\"NSViewLayoutRegion\",\"NSVisualEffectView\",\"NSWindow\",\"NSWindowController\",\"NSWindowRestoration\",\"NSWindowScripting\",\"NSWindowTab\",\"NSWindowTabGroup\",\"NSWorkspace\",\"NSWritingToolsCoordinator\",\"NSWritingToolsCoordinatorAnimationParameters\",\"NSWritingToolsCoordinatorContext\",\"bitflags\",\"block2\",\"libc\",\"objc2-cloud-kit\",\"objc2-core-data\",\"objc2-core-foundation\",\"objc2-core-graphics\",\"objc2-core-image\",\"objc2-core-text\",\"objc2-core-video\",\"objc2-quartz-core\"],\"gnustep-1-7\":[\"objc2/gnustep-1-7\",\"block2?/gnustep-1-7\",\"objc2-foundation/gnustep-1-7\",\"objc2-core-data?/gnustep-1-7\",\"objc2-quartz-core?/gnustep-1-7\"],\"gnustep-1-8\":[\"gnustep-1-7\",\"objc2/gnustep-1-8\",\"block2?/gnustep-1-8\",\"objc2-foundation/gnustep-1-8\",\"objc2-core-data?/gnustep-1-8\",\"objc2-quartz-core?/gnustep-1-8\"],\"gnustep-1-9\":[\"gnustep-1-8\",\"objc2/gnustep-1-9\",\"block2?/gnustep-1-9\",\"objc2-foundation/gnustep-1-9\",\"objc2-core-data?/gnustep-1-9\",\"objc2-quartz-core?/gnustep-1-9\"],\"gnustep-2-0\":[\"gnustep-1-9\",\"objc2/gnustep-2-0\",\"block2?/gnustep-2-0\",\"objc2-foundation/gnustep-2-0\",\"objc2-core-data?/gnustep-2-0\",\"objc2-quartz-core?/gnustep-2-0\"],\"gnustep-2-1\":[\"gnustep-2-0\",\"objc2/gnustep-2-1\",\"block2?/gnustep-2-1\",\"objc2-foundation/gnustep-2-1\",\"objc2-core-data?/gnustep-2-1\",\"objc2-quartz-core?/gnustep-2-1\"],\"libc\":[\"dep:libc\"],\"objc2-cloud-kit\":[\"dep:objc2-cloud-kit\"],\"objc2-core-data\":[\"dep:objc2-core-data\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-core-graphics\":[\"dep:objc2-core-graphics\"],\"objc2-core-image\":[\"dep:objc2-core-image\"],\"objc2-core-text\":[\"dep:objc2-core-text\"],\"objc2-core-video\":[\"dep:objc2-core-video\"],\"objc2-open-gl\":[\"dep:objc2-open-gl\"],\"objc2-quartz-core\":[\"dep:objc2-quartz-core\"],\"objc2-uniform-type-identifiers\":[\"dep:objc2-uniform-type-identifiers\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-cloud-kit_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CNContact\"],\"name\":\"objc2-contacts\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(target_os = \\\"tvos\\\"))\"},{\"default_features\":false,\"features\":[\"CLLocation\"],\"name\":\"objc2-core-location\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"}],\"features\":{\"CKAcceptSharesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKAllowedSharingOptions\":[\"bitflags\",\"objc2-foundation/NSObject\"],\"CKAsset\":[\"objc2-foundation/NSURL\"],\"CKContainer\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CKDatabase\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKDatabaseOperation\":[\"objc2-foundation/NSOperation\"],\"CKDefines\":[],\"CKDiscoverAllUserIdentitiesOperation\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKDiscoverUserIdentitiesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKError\":[\"objc2-foundation/NSString\"],\"CKFetchDatabaseChangesOperation\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKFetchNotificationChangesOperation\":[\"objc2-foundation/NSOperation\"],\"CKFetchRecordChangesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKFetchRecordZoneChangesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKFetchRecordZonesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKFetchRecordsOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKFetchShareMetadataOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CKFetchShareParticipantsOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKFetchSubscriptionsOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKFetchWebAuthTokenOperation\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKLocationSortDescriptor\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\",\"objc2-foundation/block2\"],\"CKMarkNotificationsReadOperation\":[\"objc2-foundation/NSOperation\"],\"CKModifyBadgeOperation\":[\"objc2-foundation/NSOperation\"],\"CKModifyRecordZonesOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKModifyRecordsOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\"],\"CKModifySubscriptionsOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKNotification\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"CKOperation\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKOperationGroup\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKQuery\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\"],\"CKQueryOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"CKRecord\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"CKRecordID\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKRecordZone\":[\"bitflags\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKRecordZoneID\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKReference\":[\"objc2-foundation/NSObject\"],\"CKServerChangeToken\":[\"objc2-foundation/NSObject\"],\"CKShare\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CKShareAccessRequester\":[\"objc2-foundation/NSObject\"],\"CKShareBlockedIdentity\":[\"objc2-foundation/NSObject\"],\"CKShareMetadata\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKShareParticipant\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CKShareRequestAccessOperation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSURL\"],\"CKSubscription\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"CKSyncEngine\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\"],\"CKSyncEngineConfiguration\":[\"objc2-foundation/NSString\"],\"CKSyncEngineEvent\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"CKSyncEngineRecordZoneChangeBatch\":[\"objc2-foundation/NSArray\"],\"CKSyncEngineState\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"CKSystemSharingUIObserver\":[\"objc2-foundation/NSError\"],\"CKUserIdentity\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPersonNameComponents\",\"objc2-foundation/NSString\"],\"CKUserIdentityLookupInfo\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSItemProvider_CKSharingSupport\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSItemProvider\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CKAcceptSharesOperation\",\"CKAllowedSharingOptions\",\"CKAsset\",\"CKContainer\",\"CKDatabase\",\"CKDatabaseOperation\",\"CKDefines\",\"CKDiscoverAllUserIdentitiesOperation\",\"CKDiscoverUserIdentitiesOperation\",\"CKError\",\"CKFetchDatabaseChangesOperation\",\"CKFetchNotificationChangesOperation\",\"CKFetchRecordChangesOperation\",\"CKFetchRecordZoneChangesOperation\",\"CKFetchRecordZonesOperation\",\"CKFetchRecordsOperation\",\"CKFetchShareMetadataOperation\",\"CKFetchShareParticipantsOperation\",\"CKFetchSubscriptionsOperation\",\"CKFetchWebAuthTokenOperation\",\"CKLocationSortDescriptor\",\"CKMarkNotificationsReadOperation\",\"CKModifyBadgeOperation\",\"CKModifyRecordZonesOperation\",\"CKModifyRecordsOperation\",\"CKModifySubscriptionsOperation\",\"CKNotification\",\"CKOperation\",\"CKOperationGroup\",\"CKQuery\",\"CKQueryOperation\",\"CKRecord\",\"CKRecordID\",\"CKRecordZone\",\"CKRecordZoneID\",\"CKReference\",\"CKServerChangeToken\",\"CKShare\",\"CKShareAccessRequester\",\"CKShareBlockedIdentity\",\"CKShareMetadata\",\"CKShareParticipant\",\"CKShareRequestAccessOperation\",\"CKSubscription\",\"CKSyncEngine\",\"CKSyncEngineConfiguration\",\"CKSyncEngineEvent\",\"CKSyncEngineRecordZoneChangeBatch\",\"CKSyncEngineState\",\"CKSystemSharingUIObserver\",\"CKUserIdentity\",\"CKUserIdentityLookupInfo\",\"NSItemProvider_CKSharingSupport\",\"bitflags\",\"block2\",\"objc2-contacts\",\"objc2-core-location\"],\"objc2-contacts\":[\"dep:objc2-contacts\"],\"objc2-core-location\":[\"dep:objc2-core-location\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-core-data_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CKContainer\",\"CKDatabase\",\"CKRecord\",\"CKRecordID\",\"CKRecordZoneID\",\"CKShare\",\"CKShareMetadata\",\"CKShareParticipant\",\"CKUserIdentityLookupInfo\"],\"name\":\"objc2-cloud-kit\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CSSearchableIndex\",\"CSSearchableItemAttributeSet\"],\"name\":\"objc2-core-spotlight\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(any(target_os = \\\"tvos\\\", target_os = \\\"watchos\\\")))\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"}],\"features\":{\"CloudKit\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\"],\"CoreDataDefines\":[],\"CoreDataErrors\":[\"objc2-foundation/NSString\"],\"NSAtomicStore\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSAtomicStoreCacheNode\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSAttributeDescription\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSBatchDeleteRequest\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"NSBatchInsertRequest\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSBatchUpdateRequest\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"NSCompositeAttributeDescription\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"NSCoreDataCoreSpotlightDelegate\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"NSCustomMigrationStage\":[\"objc2-foundation/NSError\"],\"NSDerivedAttributeDescription\":[\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\"],\"NSEntityDescription\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSEnumerator\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSEntityMapping\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSString\"],\"NSEntityMigrationPolicy\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"NSExpressionDescription\":[\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\"],\"NSFetchIndexDescription\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"NSFetchIndexElementDescription\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSFetchRequest\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSSortDescriptor\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"NSFetchRequestExpression\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSObject\"],\"NSFetchedPropertyDescription\":[\"objc2-foundation/NSObject\"],\"NSFetchedResultsController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSOrderedCollectionDifference\",\"objc2-foundation/NSString\",\"NSFetchRequest\"],\"NSIncrementalStore\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSIncrementalStoreNode\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSLightweightMigrationStage\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSManagedObject\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSKeyValueObserving\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"NSManagedObjectContext\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSLock\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUndoManager\"],\"NSManagedObjectID\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSURL\"],\"NSManagedObjectModel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSEnumerator\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSManagedObjectModelReference\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSMappingModel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSMergePolicy\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"NSMigrationManager\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSMigrationStage\":[\"objc2-foundation/NSString\"],\"NSPersistentCloudKitContainer\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"NSPersistentCloudKitContainerEvent\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUUID\"],\"NSPersistentCloudKitContainerEventRequest\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"NSPersistentCloudKitContainerOptions\":[\"objc2-foundation/NSString\"],\"NSPersistentContainer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPersistentHistoryChange\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\"],\"NSPersistentHistoryChangeRequest\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"NSPersistentHistoryToken\":[\"objc2-foundation/NSObject\"],\"NSPersistentHistoryTransaction\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSPersistentStore\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPersistentStoreCoordinator\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSLock\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSValue\"],\"NSPersistentStoreDescription\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSPersistentStoreRequest\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"NSPersistentStoreResult\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSProgress\",\"NSFetchRequest\"],\"NSPropertyDescription\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"NSPropertyMapping\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSExpression\",\"objc2-foundation/NSString\"],\"NSQueryGenerationToken\":[\"objc2-foundation/NSObject\"],\"NSRelationshipDescription\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\"],\"NSSaveChangesRequest\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\"],\"NSStagedMigrationManager\":[\"objc2-foundation/NSArray\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CloudKit\",\"CoreDataDefines\",\"CoreDataErrors\",\"NSAtomicStore\",\"NSAtomicStoreCacheNode\",\"NSAttributeDescription\",\"NSBatchDeleteRequest\",\"NSBatchInsertRequest\",\"NSBatchUpdateRequest\",\"NSCompositeAttributeDescription\",\"NSCoreDataCoreSpotlightDelegate\",\"NSCustomMigrationStage\",\"NSDerivedAttributeDescription\",\"NSEntityDescription\",\"NSEntityMapping\",\"NSEntityMigrationPolicy\",\"NSExpressionDescription\",\"NSFetchIndexDescription\",\"NSFetchIndexElementDescription\",\"NSFetchRequest\",\"NSFetchRequestExpression\",\"NSFetchedPropertyDescription\",\"NSFetchedResultsController\",\"NSIncrementalStore\",\"NSIncrementalStoreNode\",\"NSLightweightMigrationStage\",\"NSManagedObject\",\"NSManagedObjectContext\",\"NSManagedObjectID\",\"NSManagedObjectModel\",\"NSManagedObjectModelReference\",\"NSMappingModel\",\"NSMergePolicy\",\"NSMigrationManager\",\"NSMigrationStage\",\"NSPersistentCloudKitContainer\",\"NSPersistentCloudKitContainerEvent\",\"NSPersistentCloudKitContainerEventRequest\",\"NSPersistentCloudKitContainerOptions\",\"NSPersistentContainer\",\"NSPersistentHistoryChange\",\"NSPersistentHistoryChangeRequest\",\"NSPersistentHistoryToken\",\"NSPersistentHistoryTransaction\",\"NSPersistentStore\",\"NSPersistentStoreCoordinator\",\"NSPersistentStoreDescription\",\"NSPersistentStoreRequest\",\"NSPersistentStoreResult\",\"NSPropertyDescription\",\"NSPropertyMapping\",\"NSQueryGenerationToken\",\"NSRelationshipDescription\",\"NSSaveChangesRequest\",\"NSStagedMigrationManager\",\"bitflags\",\"block2\",\"objc2-cloud-kit\"],\"gnustep-1-7\":[],\"gnustep-1-8\":[],\"gnustep-1-9\":[],\"gnustep-2-0\":[],\"gnustep-2-1\":[],\"objc2-cloud-kit\":[\"dep:objc2-cloud-kit\"],\"objc2-core-spotlight\":[\"dep:objc2-core-spotlight\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-core-foundation_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"dispatch2\",\"optional\":true,\"req\":\">=0.3.0, <0.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.2, <0.8.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"CFArray\":[],\"CFAttributedString\":[],\"CFAvailability\":[],\"CFBag\":[],\"CFBase\":[],\"CFBinaryHeap\":[],\"CFBitVector\":[],\"CFBundle\":[],\"CFByteOrder\":[],\"CFCGTypes\":[],\"CFCalendar\":[\"bitflags\"],\"CFCharacterSet\":[],\"CFData\":[\"bitflags\"],\"CFDate\":[\"bitflags\"],\"CFDateFormatter\":[\"bitflags\"],\"CFDictionary\":[],\"CFError\":[],\"CFFileDescriptor\":[],\"CFFileSecurity\":[\"bitflags\"],\"CFLocale\":[],\"CFMachPort\":[],\"CFMessagePort\":[],\"CFNotificationCenter\":[],\"CFNumber\":[],\"CFNumberFormatter\":[\"bitflags\"],\"CFPlugIn\":[],\"CFPlugInCOM\":[],\"CFPreferences\":[],\"CFPropertyList\":[\"bitflags\"],\"CFRunLoop\":[\"bitflags\"],\"CFSet\":[],\"CFSocket\":[\"bitflags\"],\"CFStream\":[\"bitflags\"],\"CFString\":[\"bitflags\"],\"CFStringEncodingExt\":[],\"CFStringTokenizer\":[\"bitflags\"],\"CFTimeZone\":[],\"CFTree\":[],\"CFURL\":[\"bitflags\"],\"CFURLAccess\":[],\"CFURLEnumerator\":[\"bitflags\"],\"CFUUID\":[],\"CFUserNotification\":[],\"CFUtilities\":[],\"CFXMLNode\":[],\"CFXMLParser\":[\"bitflags\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CFArray\",\"CFAttributedString\",\"CFAvailability\",\"CFBag\",\"CFBinaryHeap\",\"CFBitVector\",\"CFBundle\",\"CFByteOrder\",\"CFCGTypes\",\"CFCalendar\",\"CFCharacterSet\",\"CFData\",\"CFDate\",\"CFDateFormatter\",\"CFDictionary\",\"CFError\",\"CFFileDescriptor\",\"CFFileSecurity\",\"CFLocale\",\"CFMachPort\",\"CFMessagePort\",\"CFNotificationCenter\",\"CFNumber\",\"CFNumberFormatter\",\"CFPlugIn\",\"CFPlugInCOM\",\"CFPreferences\",\"CFPropertyList\",\"CFRunLoop\",\"CFSet\",\"CFSocket\",\"CFStream\",\"CFString\",\"CFStringEncodingExt\",\"CFStringTokenizer\",\"CFTimeZone\",\"CFTree\",\"CFURL\",\"CFURLAccess\",\"CFURLEnumerator\",\"CFUUID\",\"CFUserNotification\",\"CFUtilities\",\"CFXMLNode\",\"CFXMLParser\",\"bitflags\",\"block2\",\"dispatch2\",\"libc\",\"objc2\"],\"dispatch2\":[\"dep:dispatch2\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\",\"dispatch2?/objc2\"],\"std\":[\"alloc\"],\"unstable-coerce-pointee\":[],\"unstable-darwin-objc\":[]}}", + "objc2-core-graphics_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"dispatch2\",\"optional\":true,\"req\":\">=0.3.0, <0.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"name\":\"objc2-core-foundation\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"IOSurfaceRef\"],\"name\":\"objc2-io-surface\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(target_os = \\\"watchos\\\"))\"},{\"default_features\":false,\"features\":[\"MTLDevice\"],\"name\":\"objc2-metal\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(target_os = \\\"watchos\\\"))\"}],\"features\":{\"CGAffineTransform\":[\"objc2-core-foundation/CFCGTypes\"],\"CGBase\":[],\"CGBitmapContext\":[\"bitflags\",\"objc2-core-foundation/CFByteOrder\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFError\"],\"CGColor\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGColorConversionInfo\":[\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFError\"],\"CGColorSpace\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\"],\"CGContext\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGConvertColorDataWithFormat\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGDataConsumer\":[\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFURL\"],\"CGDataProvider\":[\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFURL\"],\"CGDirectDisplay\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGDirectDisplayMetal\":[],\"CGDirectPalette\":[],\"CGDisplayConfiguration\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGDisplayFade\":[],\"CGDisplayStream\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFRunLoop\"],\"CGEXRToneMappingGamma\":[\"objc2-core-foundation/CFDictionary\"],\"CGError\":[],\"CGEvent\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFMachPort\"],\"CGEventSource\":[\"objc2-core-foundation/CFDate\"],\"CGEventTypes\":[\"bitflags\"],\"CGFont\":[\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFDictionary\"],\"CGFunction\":[\"objc2-core-foundation/CFCGTypes\"],\"CGGeometry\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGGradient\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\"],\"CGITUToneMapping\":[],\"CGImage\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\"],\"CGLayer\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CGPDFArray\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFContentStream\":[\"objc2-core-foundation/CFArray\"],\"CGPDFContext\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFURL\"],\"CGPDFDictionary\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFDocument\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFURL\"],\"CGPDFObject\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFOperatorTable\":[],\"CGPDFPage\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFScanner\":[\"objc2-core-foundation/CFCGTypes\"],\"CGPDFStream\":[\"objc2-core-foundation/CFData\"],\"CGPDFString\":[\"objc2-core-foundation/CFDate\"],\"CGPSConverter\":[\"objc2-core-foundation/CFDictionary\"],\"CGPath\":[\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\"],\"CGPattern\":[\"objc2-core-foundation/CFCGTypes\"],\"CGRemoteOperation\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDate\",\"objc2-core-foundation/CFMachPort\"],\"CGRenderingBufferProvider\":[\"objc2-core-foundation/CFData\"],\"CGSession\":[\"objc2-core-foundation/CFDictionary\"],\"CGShading\":[\"objc2-core-foundation/CFCGTypes\"],\"CGToneMapping\":[\"objc2-core-foundation/CFDictionary\"],\"CGWindow\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\"],\"CGWindowLevel\":[],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CGAffineTransform\",\"CGBase\",\"CGBitmapContext\",\"CGColor\",\"CGColorConversionInfo\",\"CGColorSpace\",\"CGContext\",\"CGConvertColorDataWithFormat\",\"CGDataConsumer\",\"CGDataProvider\",\"CGDirectDisplay\",\"CGDirectDisplayMetal\",\"CGDirectPalette\",\"CGDisplayConfiguration\",\"CGDisplayFade\",\"CGDisplayStream\",\"CGEXRToneMappingGamma\",\"CGError\",\"CGEvent\",\"CGEventSource\",\"CGEventTypes\",\"CGFont\",\"CGFunction\",\"CGGeometry\",\"CGGradient\",\"CGITUToneMapping\",\"CGImage\",\"CGLayer\",\"CGPDFArray\",\"CGPDFContentStream\",\"CGPDFContext\",\"CGPDFDictionary\",\"CGPDFDocument\",\"CGPDFObject\",\"CGPDFOperatorTable\",\"CGPDFPage\",\"CGPDFScanner\",\"CGPDFStream\",\"CGPDFString\",\"CGPSConverter\",\"CGPath\",\"CGPattern\",\"CGRemoteOperation\",\"CGRenderingBufferProvider\",\"CGSession\",\"CGShading\",\"CGToneMapping\",\"CGWindow\",\"CGWindowLevel\",\"bitflags\",\"block2\",\"dispatch2\",\"libc\",\"objc2\",\"objc2-metal\"],\"dispatch2\":[\"dep:dispatch2\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\",\"dispatch2?/objc2\",\"objc2-core-foundation/objc2\",\"objc2-io-surface?/objc2\"],\"objc2-io-surface\":[\"dep:objc2-io-surface\"],\"objc2-metal\":[\"dep:objc2-metal\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-core-image_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CFCGTypes\",\"CFDictionary\",\"objc2\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGColor\",\"CGColorSpace\",\"CGContext\",\"CGImage\",\"CGLayer\",\"objc2\"],\"name\":\"objc2-core-graphics\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"MLModel\"],\"name\":\"objc2-core-ml\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CVBuffer\",\"CVImageBuffer\",\"CVPixelBuffer\",\"objc2\"],\"name\":\"objc2-core-video\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGImageProperties\",\"CGImageSource\",\"objc2\"],\"name\":\"objc2-image-io\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"IOSurfaceRef\",\"objc2\"],\"name\":\"objc2-io-surface\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"MTLAllocation\",\"MTLCommandBuffer\",\"MTLCommandQueue\",\"MTLDevice\",\"MTLPixelFormat\",\"MTLResource\",\"MTLTexture\"],\"name\":\"objc2-metal\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGLTypes\"],\"name\":\"objc2-open-gl\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_os = \\\"macos\\\")\"}],\"features\":{\"CIBarcodeDescriptor\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSUserActivity\"],\"CIColor\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CIContext\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CIDetector\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"CIFeature\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CIFilter\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CIFilterBuiltins\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSString\"],\"CIFilterConstructor\":[\"objc2-foundation/NSString\"],\"CIFilterGenerator\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CIFilterShape\":[\"objc2-foundation/NSObject\"],\"CIImage\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CIImageAccumulator\":[],\"CIImageProcessor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"CIImageProvider\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"CIKernel\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"CIKernelMetalLib\":[],\"CIPlugIn\":[\"objc2-foundation/NSURL\"],\"CIPlugInInterface\":[],\"CIRAWFilter\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CIRAWFilter_Deprecated\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"CIRenderDestination\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\",\"objc2-foundation/NSURL\"],\"CISampler\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CIVector\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CoreImageDefines\":[],\"alloc\":[],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CIBarcodeDescriptor\",\"CIColor\",\"CIContext\",\"CIDetector\",\"CIFeature\",\"CIFilter\",\"CIFilterBuiltins\",\"CIFilterConstructor\",\"CIFilterGenerator\",\"CIFilterShape\",\"CIImage\",\"CIImageAccumulator\",\"CIImageProcessor\",\"CIImageProvider\",\"CIKernel\",\"CIKernelMetalLib\",\"CIPlugIn\",\"CIPlugInInterface\",\"CIRAWFilter\",\"CIRAWFilter_Deprecated\",\"CIRenderDestination\",\"CISampler\",\"CIVector\",\"CoreImageDefines\",\"block2\",\"objc2-core-foundation\",\"objc2-core-graphics\",\"objc2-core-video\",\"objc2-image-io\",\"objc2-metal\"],\"gnustep-1-7\":[\"objc2/gnustep-1-7\",\"block2?/gnustep-1-7\",\"objc2-foundation/gnustep-1-7\"],\"gnustep-1-8\":[\"gnustep-1-7\",\"objc2/gnustep-1-8\",\"block2?/gnustep-1-8\",\"objc2-foundation/gnustep-1-8\"],\"gnustep-1-9\":[\"gnustep-1-8\",\"objc2/gnustep-1-9\",\"block2?/gnustep-1-9\",\"objc2-foundation/gnustep-1-9\"],\"gnustep-2-0\":[\"gnustep-1-9\",\"objc2/gnustep-2-0\",\"block2?/gnustep-2-0\",\"objc2-foundation/gnustep-2-0\"],\"gnustep-2-1\":[\"gnustep-2-0\",\"objc2/gnustep-2-1\",\"block2?/gnustep-2-1\",\"objc2-foundation/gnustep-2-1\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-core-graphics\":[\"dep:objc2-core-graphics\"],\"objc2-core-ml\":[\"dep:objc2-core-ml\"],\"objc2-core-video\":[\"dep:objc2-core-video\"],\"objc2-image-io\":[\"dep:objc2-image-io\"],\"objc2-io-surface\":[\"dep:objc2-io-surface\"],\"objc2-metal\":[\"dep:objc2-metal\"],\"objc2-open-gl\":[\"dep:objc2-open-gl\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-core-location_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\",\"objc2\"],\"name\":\"dispatch2\",\"optional\":true,\"req\":\">=0.3.0, <0.5.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CNPostalAddress\"],\"name\":\"objc2-contacts\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(target_os = \\\"tvos\\\"))\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"}],\"features\":{\"CLAvailability\":[],\"CLBackgroundActivitySession\":[],\"CLBeaconIdentityCondition\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSUUID\",\"objc2-foundation/NSValue\"],\"CLBeaconIdentityConstraint\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSUUID\"],\"CLBeaconRegion\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUUID\",\"objc2-foundation/NSValue\"],\"CLCircularGeographicCondition\":[\"objc2-foundation/NSObject\"],\"CLCircularRegion\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CLCondition\":[\"objc2-foundation/NSObject\"],\"CLError\":[\"objc2-foundation/NSString\"],\"CLErrorDomain\":[\"objc2-foundation/NSString\"],\"CLGeocoder\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSString\"],\"CLHeading\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"CLLocation\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"CLLocationManager\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"CLLocationManagerDelegate\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\"],\"CLLocationManager_CLVisitExtensions\":[],\"CLLocationPushServiceError\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"CLLocationPushServiceExtension\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"CLLocationUpdater\":[],\"CLMonitor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"CLMonitorConfiguration\":[\"objc2-foundation/NSString\"],\"CLMonitoringEvent\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CLMonitoringRecord\":[\"objc2-foundation/NSObject\"],\"CLPlacemark\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTimeZone\"],\"CLRegion\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CLServiceSession\":[\"objc2-foundation/NSString\"],\"CLVisit\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"alloc\":[],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CLAvailability\",\"CLBackgroundActivitySession\",\"CLBeaconIdentityCondition\",\"CLBeaconIdentityConstraint\",\"CLBeaconRegion\",\"CLCircularGeographicCondition\",\"CLCircularRegion\",\"CLCondition\",\"CLError\",\"CLErrorDomain\",\"CLGeocoder\",\"CLHeading\",\"CLLocation\",\"CLLocationManager\",\"CLLocationManagerDelegate\",\"CLLocationManager_CLVisitExtensions\",\"CLLocationPushServiceError\",\"CLLocationPushServiceExtension\",\"CLLocationUpdater\",\"CLMonitor\",\"CLMonitorConfiguration\",\"CLMonitoringEvent\",\"CLMonitoringRecord\",\"CLPlacemark\",\"CLRegion\",\"CLServiceSession\",\"CLVisit\",\"block2\",\"dispatch2\",\"objc2-contacts\"],\"dispatch2\":[\"dep:dispatch2\"],\"objc2-contacts\":[\"dep:objc2-contacts\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-core-text_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"name\":\"objc2-core-foundation\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGContext\",\"CGFont\",\"CGImage\",\"CGPath\"],\"name\":\"objc2-core-graphics\",\"optional\":true,\"req\":\"^0.3.2\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"CTDefines\":[],\"CTFont\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFCharacterSet\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFString\"],\"CTFontCollection\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFSet\"],\"CTFontDescriptor\":[\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFNumber\",\"objc2-core-foundation/CFSet\"],\"CTFontManager\":[\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFBundle\",\"objc2-core-foundation/CFData\",\"objc2-core-foundation/CFDictionary\",\"objc2-core-foundation/CFError\",\"objc2-core-foundation/CFRunLoop\",\"objc2-core-foundation/CFURL\"],\"CTFontManagerErrors\":[],\"CTFontTraits\":[\"bitflags\"],\"CTFrame\":[\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CTFramesetter\":[\"objc2-core-foundation/CFAttributedString\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CTGlyphInfo\":[],\"CTLine\":[\"bitflags\",\"objc2-core-foundation/CFArray\",\"objc2-core-foundation/CFAttributedString\",\"objc2-core-foundation/CFCGTypes\"],\"CTParagraphStyle\":[],\"CTRubyAnnotation\":[\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CTRun\":[\"bitflags\",\"objc2-core-foundation/CFCGTypes\",\"objc2-core-foundation/CFDictionary\"],\"CTRunDelegate\":[\"objc2-core-foundation/CFCGTypes\"],\"CTStringAttributes\":[\"bitflags\"],\"CTTextTab\":[\"objc2-core-foundation/CFDictionary\"],\"CTTypesetter\":[\"objc2-core-foundation/CFAttributedString\",\"objc2-core-foundation/CFDictionary\"],\"SFNTLayoutTypes\":[],\"SFNTTypes\":[],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CTDefines\",\"CTFont\",\"CTFontCollection\",\"CTFontDescriptor\",\"CTFontManager\",\"CTFontManagerErrors\",\"CTFontTraits\",\"CTFrame\",\"CTFramesetter\",\"CTGlyphInfo\",\"CTLine\",\"CTParagraphStyle\",\"CTRubyAnnotation\",\"CTRun\",\"CTRunDelegate\",\"CTStringAttributes\",\"CTTextTab\",\"CTTypesetter\",\"SFNTLayoutTypes\",\"SFNTTypes\",\"bitflags\",\"block2\",\"libc\",\"objc2\",\"objc2-core-graphics\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\",\"objc2-core-foundation/objc2\",\"objc2-core-graphics?/objc2\"],\"objc2-core-graphics\":[\"dep:objc2-core-graphics\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", "objc2-encode_4.1.0": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", - "objc2-foundation_0.3.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"CFCGTypes\",\"CFRunLoop\",\"objc2\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.1\"},{\"default_features\":false,\"features\":[\"AE\",\"AEDataModel\",\"objc2\"],\"name\":\"objc2-core-services\",\"optional\":true,\"req\":\"^0.3.1\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"FoundationErrors\":[],\"FoundationLegacySwiftCompatibility\":[],\"NSAffineTransform\":[],\"NSAppleEventDescriptor\":[\"bitflags\"],\"NSAppleEventManager\":[],\"NSAppleScript\":[],\"NSArchiver\":[],\"NSArray\":[\"bitflags\"],\"NSAttributedString\":[\"bitflags\"],\"NSAutoreleasePool\":[],\"NSBackgroundActivityScheduler\":[],\"NSBundle\":[],\"NSByteCountFormatter\":[\"bitflags\"],\"NSByteOrder\":[],\"NSCache\":[],\"NSCalendar\":[\"bitflags\"],\"NSCalendarDate\":[],\"NSCharacterSet\":[],\"NSClassDescription\":[],\"NSCoder\":[],\"NSComparisonPredicate\":[\"bitflags\"],\"NSCompoundPredicate\":[],\"NSConnection\":[],\"NSData\":[\"bitflags\"],\"NSDate\":[],\"NSDateComponentsFormatter\":[\"bitflags\"],\"NSDateFormatter\":[],\"NSDateInterval\":[],\"NSDateIntervalFormatter\":[],\"NSDebug\":[],\"NSDecimal\":[],\"NSDecimalNumber\":[],\"NSDictionary\":[],\"NSDistantObject\":[],\"NSDistributedLock\":[],\"NSDistributedNotificationCenter\":[\"bitflags\"],\"NSEnergyFormatter\":[],\"NSEnumerator\":[],\"NSError\":[],\"NSException\":[],\"NSExpression\":[],\"NSExtensionContext\":[],\"NSExtensionItem\":[],\"NSExtensionRequestHandling\":[],\"NSFileCoordinator\":[\"bitflags\"],\"NSFileHandle\":[],\"NSFileManager\":[\"bitflags\"],\"NSFilePresenter\":[],\"NSFileVersion\":[\"bitflags\"],\"NSFileWrapper\":[\"bitflags\"],\"NSFormatter\":[],\"NSGarbageCollector\":[],\"NSGeometry\":[\"bitflags\"],\"NSHFSFileTypes\":[],\"NSHTTPCookie\":[],\"NSHTTPCookieStorage\":[],\"NSHashTable\":[],\"NSHost\":[],\"NSISO8601DateFormatter\":[\"bitflags\"],\"NSIndexPath\":[],\"NSIndexSet\":[],\"NSInflectionRule\":[],\"NSInvocation\":[],\"NSItemProvider\":[\"bitflags\"],\"NSJSONSerialization\":[\"bitflags\"],\"NSKeyValueCoding\":[],\"NSKeyValueObserving\":[\"bitflags\"],\"NSKeyValueSharedObservers\":[],\"NSKeyedArchiver\":[],\"NSLengthFormatter\":[],\"NSLinguisticTagger\":[\"bitflags\"],\"NSListFormatter\":[],\"NSLocale\":[],\"NSLocalizedNumberFormatRule\":[],\"NSLock\":[],\"NSMapTable\":[],\"NSMassFormatter\":[],\"NSMeasurement\":[],\"NSMeasurementFormatter\":[\"bitflags\"],\"NSMetadata\":[],\"NSMetadataAttributes\":[],\"NSMethodSignature\":[],\"NSMorphology\":[],\"NSNetServices\":[\"bitflags\"],\"NSNotification\":[],\"NSNotificationQueue\":[\"bitflags\"],\"NSNull\":[],\"NSNumberFormatter\":[],\"NSObjCRuntime\":[\"bitflags\"],\"NSObject\":[],\"NSObjectScripting\":[],\"NSOperation\":[],\"NSOrderedCollectionChange\":[],\"NSOrderedCollectionDifference\":[\"bitflags\"],\"NSOrderedSet\":[],\"NSOrthography\":[],\"NSPathUtilities\":[\"bitflags\"],\"NSPersonNameComponents\":[],\"NSPersonNameComponentsFormatter\":[\"bitflags\"],\"NSPointerArray\":[],\"NSPointerFunctions\":[\"bitflags\"],\"NSPort\":[\"bitflags\"],\"NSPortCoder\":[],\"NSPortMessage\":[],\"NSPortNameServer\":[],\"NSPredicate\":[],\"NSProcessInfo\":[\"bitflags\"],\"NSProgress\":[],\"NSPropertyList\":[\"bitflags\"],\"NSProtocolChecker\":[],\"NSProxy\":[],\"NSRange\":[],\"NSRegularExpression\":[\"bitflags\"],\"NSRelativeDateTimeFormatter\":[],\"NSRunLoop\":[],\"NSScanner\":[],\"NSScriptClassDescription\":[],\"NSScriptCoercionHandler\":[],\"NSScriptCommand\":[],\"NSScriptCommandDescription\":[],\"NSScriptExecutionContext\":[],\"NSScriptKeyValueCoding\":[],\"NSScriptObjectSpecifiers\":[],\"NSScriptStandardSuiteCommands\":[],\"NSScriptSuiteRegistry\":[],\"NSScriptWhoseTests\":[],\"NSSet\":[],\"NSSortDescriptor\":[],\"NSSpellServer\":[],\"NSStream\":[\"bitflags\"],\"NSString\":[\"bitflags\"],\"NSTask\":[],\"NSTermOfAddress\":[],\"NSTextCheckingResult\":[\"bitflags\"],\"NSThread\":[],\"NSTimeZone\":[],\"NSTimer\":[],\"NSURL\":[\"bitflags\"],\"NSURLAuthenticationChallenge\":[],\"NSURLCache\":[],\"NSURLConnection\":[],\"NSURLCredential\":[],\"NSURLCredentialStorage\":[],\"NSURLDownload\":[],\"NSURLError\":[],\"NSURLHandle\":[],\"NSURLProtectionSpace\":[],\"NSURLProtocol\":[],\"NSURLRequest\":[],\"NSURLResponse\":[],\"NSURLSession\":[],\"NSUUID\":[],\"NSUbiquitousKeyValueStore\":[],\"NSUndoManager\":[],\"NSUnit\":[],\"NSUserActivity\":[],\"NSUserDefaults\":[],\"NSUserNotification\":[],\"NSUserScriptTask\":[],\"NSValue\":[],\"NSValueTransformer\":[],\"NSXMLDTD\":[],\"NSXMLDTDNode\":[],\"NSXMLDocument\":[],\"NSXMLElement\":[],\"NSXMLNode\":[],\"NSXMLNodeOptions\":[\"bitflags\"],\"NSXMLParser\":[],\"NSXPCConnection\":[\"bitflags\"],\"NSZone\":[],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"FoundationErrors\",\"FoundationLegacySwiftCompatibility\",\"NSAffineTransform\",\"NSAppleEventDescriptor\",\"NSAppleEventManager\",\"NSAppleScript\",\"NSArchiver\",\"NSArray\",\"NSAttributedString\",\"NSAutoreleasePool\",\"NSBackgroundActivityScheduler\",\"NSBundle\",\"NSByteCountFormatter\",\"NSByteOrder\",\"NSCache\",\"NSCalendar\",\"NSCalendarDate\",\"NSCharacterSet\",\"NSClassDescription\",\"NSCoder\",\"NSComparisonPredicate\",\"NSCompoundPredicate\",\"NSConnection\",\"NSData\",\"NSDate\",\"NSDateComponentsFormatter\",\"NSDateFormatter\",\"NSDateInterval\",\"NSDateIntervalFormatter\",\"NSDebug\",\"NSDecimal\",\"NSDecimalNumber\",\"NSDictionary\",\"NSDistantObject\",\"NSDistributedLock\",\"NSDistributedNotificationCenter\",\"NSEnergyFormatter\",\"NSEnumerator\",\"NSError\",\"NSException\",\"NSExpression\",\"NSExtensionContext\",\"NSExtensionItem\",\"NSExtensionRequestHandling\",\"NSFileCoordinator\",\"NSFileHandle\",\"NSFileManager\",\"NSFilePresenter\",\"NSFileVersion\",\"NSFileWrapper\",\"NSFormatter\",\"NSGarbageCollector\",\"NSGeometry\",\"NSHFSFileTypes\",\"NSHTTPCookie\",\"NSHTTPCookieStorage\",\"NSHashTable\",\"NSHost\",\"NSISO8601DateFormatter\",\"NSIndexPath\",\"NSIndexSet\",\"NSInflectionRule\",\"NSInvocation\",\"NSItemProvider\",\"NSJSONSerialization\",\"NSKeyValueCoding\",\"NSKeyValueObserving\",\"NSKeyValueSharedObservers\",\"NSKeyedArchiver\",\"NSLengthFormatter\",\"NSLinguisticTagger\",\"NSListFormatter\",\"NSLocale\",\"NSLocalizedNumberFormatRule\",\"NSLock\",\"NSMapTable\",\"NSMassFormatter\",\"NSMeasurement\",\"NSMeasurementFormatter\",\"NSMetadata\",\"NSMetadataAttributes\",\"NSMethodSignature\",\"NSMorphology\",\"NSNetServices\",\"NSNotification\",\"NSNotificationQueue\",\"NSNull\",\"NSNumberFormatter\",\"NSObjCRuntime\",\"NSObject\",\"NSObjectScripting\",\"NSOperation\",\"NSOrderedCollectionChange\",\"NSOrderedCollectionDifference\",\"NSOrderedSet\",\"NSOrthography\",\"NSPathUtilities\",\"NSPersonNameComponents\",\"NSPersonNameComponentsFormatter\",\"NSPointerArray\",\"NSPointerFunctions\",\"NSPort\",\"NSPortCoder\",\"NSPortMessage\",\"NSPortNameServer\",\"NSPredicate\",\"NSProcessInfo\",\"NSProgress\",\"NSPropertyList\",\"NSProtocolChecker\",\"NSProxy\",\"NSRange\",\"NSRegularExpression\",\"NSRelativeDateTimeFormatter\",\"NSRunLoop\",\"NSScanner\",\"NSScriptClassDescription\",\"NSScriptCoercionHandler\",\"NSScriptCommand\",\"NSScriptCommandDescription\",\"NSScriptExecutionContext\",\"NSScriptKeyValueCoding\",\"NSScriptObjectSpecifiers\",\"NSScriptStandardSuiteCommands\",\"NSScriptSuiteRegistry\",\"NSScriptWhoseTests\",\"NSSet\",\"NSSortDescriptor\",\"NSSpellServer\",\"NSStream\",\"NSString\",\"NSTask\",\"NSTermOfAddress\",\"NSTextCheckingResult\",\"NSThread\",\"NSTimeZone\",\"NSTimer\",\"NSURL\",\"NSURLAuthenticationChallenge\",\"NSURLCache\",\"NSURLConnection\",\"NSURLCredential\",\"NSURLCredentialStorage\",\"NSURLDownload\",\"NSURLError\",\"NSURLHandle\",\"NSURLProtectionSpace\",\"NSURLProtocol\",\"NSURLRequest\",\"NSURLResponse\",\"NSURLSession\",\"NSUUID\",\"NSUbiquitousKeyValueStore\",\"NSUndoManager\",\"NSUnit\",\"NSUserActivity\",\"NSUserDefaults\",\"NSUserNotification\",\"NSUserScriptTask\",\"NSValue\",\"NSValueTransformer\",\"NSXMLDTD\",\"NSXMLDTDNode\",\"NSXMLDocument\",\"NSXMLElement\",\"NSXMLNode\",\"NSXMLNodeOptions\",\"NSXMLParser\",\"NSXPCConnection\",\"NSZone\",\"bitflags\",\"block2\",\"libc\",\"objc2-core-foundation\"],\"gnustep-1-7\":[\"objc2/gnustep-1-7\",\"block2?/gnustep-1-7\"],\"gnustep-1-8\":[\"gnustep-1-7\",\"objc2/gnustep-1-8\",\"block2?/gnustep-1-8\"],\"gnustep-1-9\":[\"gnustep-1-8\",\"objc2/gnustep-1-9\",\"block2?/gnustep-1-9\"],\"gnustep-2-0\":[\"gnustep-1-9\",\"objc2/gnustep-2-0\",\"block2?/gnustep-2-0\"],\"gnustep-2-1\":[\"gnustep-2-0\",\"objc2/gnustep-2-1\",\"block2?/gnustep-2-1\"],\"libc\":[\"dep:libc\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-core-services\":[\"dep:objc2-core-services\"],\"std\":[\"alloc\"],\"unstable-mutation-return-null\":[\"NSNull\"],\"unstable-static-nsstring\":[]}}", - "objc2-io-surface_0.3.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"CFDictionary\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.1\"},{\"default_features\":false,\"features\":[\"NSDictionary\",\"NSObject\",\"NSString\",\"alloc\"],\"name\":\"objc2-foundation\",\"optional\":true,\"req\":\"^0.3.1\"}],\"features\":{\"IOSurface\":[],\"IOSurfaceAPI\":[],\"IOSurfaceBase\":[],\"IOSurfaceRef\":[\"bitflags\"],\"IOSurfaceTypes\":[\"bitflags\"],\"ObjC\":[\"objc2\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"default\":[\"std\",\"IOSurface\",\"IOSurfaceAPI\",\"IOSurfaceBase\",\"IOSurfaceRef\",\"IOSurfaceTypes\",\"bitflags\",\"libc\",\"objc2\",\"objc2-core-foundation\",\"objc2-foundation\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\",\"objc2-core-foundation?/objc2\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-foundation\":[\"dep:objc2-foundation\"],\"std\":[\"alloc\"]}}", - "objc2_0.6.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"backtrace\",\"req\":\"^0.3.74\"},{\"kind\":\"dev\",\"name\":\"core-foundation\",\"req\":\"^0.10.0\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"kind\":\"dev\",\"name\":\"iai\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.158\"},{\"kind\":\"dev\",\"name\":\"memoffset\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"name\":\"objc2-encode\",\"req\":\"^4.1.0\"},{\"default_features\":false,\"name\":\"objc2-exception-helper\",\"optional\":true,\"req\":\"^0.1.1\"},{\"name\":\"objc2-proc-macros\",\"optional\":true,\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"alloc\":[\"objc2-encode/alloc\"],\"catch-all\":[\"exception\"],\"default\":[\"std\"],\"disable-encoding-assertions\":[],\"exception\":[\"dep:objc2-exception-helper\"],\"gnustep-1-7\":[\"unstable-static-class\",\"objc2-exception-helper?/gnustep-1-7\"],\"gnustep-1-8\":[\"gnustep-1-7\",\"objc2-exception-helper?/gnustep-1-8\"],\"gnustep-1-9\":[\"gnustep-1-8\",\"objc2-exception-helper?/gnustep-1-9\"],\"gnustep-2-0\":[\"gnustep-1-9\",\"objc2-exception-helper?/gnustep-2-0\"],\"gnustep-2-1\":[\"gnustep-2-0\",\"objc2-exception-helper?/gnustep-2-1\"],\"objc2-proc-macros\":[],\"relax-sign-encoding\":[],\"relax-void-encoding\":[],\"std\":[\"alloc\",\"objc2-encode/std\"],\"unstable-apple-new\":[],\"unstable-arbitrary-self-types\":[],\"unstable-autoreleasesafe\":[],\"unstable-coerce-pointee\":[],\"unstable-compiler-rt\":[\"gnustep-1-7\"],\"unstable-gnustep-strict-apple-compat\":[\"gnustep-1-7\"],\"unstable-objfw\":[],\"unstable-requires-macos\":[],\"unstable-static-class\":[\"dep:objc2-proc-macros\"],\"unstable-static-class-inlined\":[\"unstable-static-class\"],\"unstable-static-sel\":[\"dep:objc2-proc-macros\"],\"unstable-static-sel-inlined\":[\"unstable-static-sel\"],\"unstable-winobjc\":[\"gnustep-1-8\"],\"verify\":[]}}", - "object_0.36.7": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.2\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"crc32fast\",\"optional\":true,\"req\":\"^1.2\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"default-hasher\"],\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15.0\"},{\"default_features\":false,\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2.4.1\"},{\"name\":\"ruzstd\",\"optional\":true,\"req\":\"^0.7.0\"},{\"default_features\":false,\"name\":\"wasmparser\",\"optional\":true,\"req\":\"^0.222.0\"}],\"features\":{\"all\":[\"read\",\"write\",\"build\",\"std\",\"compression\",\"wasm\"],\"archive\":[],\"build\":[\"build_core\",\"write_std\",\"elf\"],\"build_core\":[\"read_core\",\"write_core\"],\"cargo-all\":[],\"coff\":[],\"compression\":[\"dep:flate2\",\"dep:ruzstd\",\"std\"],\"default\":[\"read\",\"compression\"],\"doc\":[\"read_core\",\"write_std\",\"build_core\",\"std\",\"compression\",\"archive\",\"coff\",\"elf\",\"macho\",\"pe\",\"wasm\",\"xcoff\"],\"elf\":[],\"macho\":[],\"pe\":[\"coff\"],\"read\":[\"read_core\",\"archive\",\"coff\",\"elf\",\"macho\",\"pe\",\"xcoff\",\"unaligned\"],\"read_core\":[],\"rustc-dep-of-std\":[\"core\",\"compiler_builtins\",\"alloc\",\"memchr/rustc-dep-of-std\"],\"std\":[\"memchr/std\"],\"unaligned\":[],\"unstable\":[],\"unstable-all\":[\"all\",\"unstable\"],\"wasm\":[\"dep:wasmparser\"],\"write\":[\"write_std\",\"coff\",\"elf\",\"macho\",\"pe\",\"xcoff\"],\"write_core\":[\"dep:crc32fast\",\"dep:indexmap\",\"dep:hashbrown\"],\"write_std\":[\"write_core\",\"std\",\"indexmap?/std\",\"crc32fast?/std\"],\"xcoff\":[]}}", + "objc2-foundation_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CFArray\",\"CFAttributedString\",\"CFCGTypes\",\"CFCalendar\",\"CFCharacterSet\",\"CFData\",\"CFDate\",\"CFDictionary\",\"CFError\",\"CFFileSecurity\",\"CFLocale\",\"CFMachPort\",\"CFMessagePort\",\"CFRunLoop\",\"CFSet\",\"CFStream\",\"CFURL\",\"objc2\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CFString\"],\"kind\":\"dev\",\"name\":\"objc2-core-foundation\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"AE\",\"AEDataModel\",\"objc2\"],\"name\":\"objc2-core-services\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"FoundationErrors\":[],\"FoundationLegacySwiftCompatibility\":[],\"NSAffineTransform\":[],\"NSAppleEventDescriptor\":[\"bitflags\"],\"NSAppleEventManager\":[],\"NSAppleScript\":[],\"NSArchiver\":[],\"NSArray\":[\"bitflags\"],\"NSAttributedString\":[\"bitflags\"],\"NSAutoreleasePool\":[],\"NSBackgroundActivityScheduler\":[],\"NSBundle\":[],\"NSByteCountFormatter\":[\"bitflags\"],\"NSByteOrder\":[],\"NSCache\":[],\"NSCalendar\":[\"bitflags\"],\"NSCalendarDate\":[],\"NSCharacterSet\":[],\"NSClassDescription\":[],\"NSCoder\":[],\"NSComparisonPredicate\":[\"bitflags\"],\"NSCompoundPredicate\":[],\"NSConnection\":[],\"NSData\":[\"bitflags\"],\"NSDate\":[],\"NSDateComponentsFormatter\":[\"bitflags\"],\"NSDateFormatter\":[],\"NSDateInterval\":[],\"NSDateIntervalFormatter\":[],\"NSDebug\":[],\"NSDecimal\":[],\"NSDecimalNumber\":[],\"NSDictionary\":[],\"NSDistantObject\":[],\"NSDistributedLock\":[],\"NSDistributedNotificationCenter\":[\"bitflags\"],\"NSEnergyFormatter\":[],\"NSEnumerator\":[],\"NSError\":[],\"NSException\":[],\"NSExpression\":[],\"NSExtensionContext\":[],\"NSExtensionItem\":[],\"NSExtensionRequestHandling\":[],\"NSFileCoordinator\":[\"bitflags\"],\"NSFileHandle\":[],\"NSFileManager\":[\"bitflags\"],\"NSFilePresenter\":[],\"NSFileVersion\":[\"bitflags\"],\"NSFileWrapper\":[\"bitflags\"],\"NSFormatter\":[],\"NSGarbageCollector\":[],\"NSGeometry\":[\"bitflags\"],\"NSHFSFileTypes\":[],\"NSHTTPCookie\":[],\"NSHTTPCookieStorage\":[],\"NSHashTable\":[],\"NSHost\":[],\"NSISO8601DateFormatter\":[\"bitflags\"],\"NSIndexPath\":[],\"NSIndexSet\":[],\"NSInflectionRule\":[],\"NSInvocation\":[],\"NSItemProvider\":[\"bitflags\"],\"NSJSONSerialization\":[\"bitflags\"],\"NSKeyValueCoding\":[],\"NSKeyValueObserving\":[\"bitflags\"],\"NSKeyValueSharedObservers\":[],\"NSKeyedArchiver\":[],\"NSLengthFormatter\":[],\"NSLinguisticTagger\":[\"bitflags\"],\"NSListFormatter\":[],\"NSLocale\":[],\"NSLocalizedNumberFormatRule\":[],\"NSLock\":[],\"NSMapTable\":[],\"NSMassFormatter\":[],\"NSMeasurement\":[\"NSUnit\"],\"NSMeasurementFormatter\":[\"bitflags\"],\"NSMetadata\":[],\"NSMetadataAttributes\":[],\"NSMethodSignature\":[],\"NSMorphology\":[],\"NSNetServices\":[\"bitflags\"],\"NSNotification\":[],\"NSNotificationQueue\":[\"bitflags\"],\"NSNull\":[],\"NSNumberFormatter\":[],\"NSObjCRuntime\":[\"bitflags\"],\"NSObject\":[],\"NSObjectScripting\":[],\"NSOperation\":[],\"NSOrderedCollectionChange\":[],\"NSOrderedCollectionDifference\":[\"bitflags\"],\"NSOrderedSet\":[],\"NSOrthography\":[],\"NSPathUtilities\":[\"bitflags\"],\"NSPersonNameComponents\":[],\"NSPersonNameComponentsFormatter\":[\"bitflags\"],\"NSPointerArray\":[],\"NSPointerFunctions\":[\"bitflags\"],\"NSPort\":[\"bitflags\"],\"NSPortCoder\":[],\"NSPortMessage\":[],\"NSPortNameServer\":[],\"NSPredicate\":[],\"NSProcessInfo\":[\"bitflags\"],\"NSProgress\":[],\"NSPropertyList\":[\"bitflags\"],\"NSProtocolChecker\":[],\"NSProxy\":[],\"NSRange\":[],\"NSRegularExpression\":[\"bitflags\"],\"NSRelativeDateTimeFormatter\":[],\"NSRunLoop\":[],\"NSScanner\":[],\"NSScriptClassDescription\":[],\"NSScriptCoercionHandler\":[],\"NSScriptCommand\":[],\"NSScriptCommandDescription\":[],\"NSScriptExecutionContext\":[],\"NSScriptKeyValueCoding\":[],\"NSScriptObjectSpecifiers\":[],\"NSScriptStandardSuiteCommands\":[],\"NSScriptSuiteRegistry\":[],\"NSScriptWhoseTests\":[],\"NSSet\":[],\"NSSortDescriptor\":[],\"NSSpellServer\":[],\"NSStream\":[\"bitflags\"],\"NSString\":[\"bitflags\"],\"NSTask\":[],\"NSTermOfAddress\":[],\"NSTextCheckingResult\":[\"bitflags\"],\"NSThread\":[],\"NSTimeZone\":[],\"NSTimer\":[],\"NSURL\":[\"bitflags\"],\"NSURLAuthenticationChallenge\":[],\"NSURLCache\":[],\"NSURLConnection\":[],\"NSURLCredential\":[],\"NSURLCredentialStorage\":[],\"NSURLDownload\":[],\"NSURLError\":[],\"NSURLHandle\":[],\"NSURLProtectionSpace\":[],\"NSURLProtocol\":[],\"NSURLRequest\":[],\"NSURLResponse\":[],\"NSURLSession\":[],\"NSUUID\":[],\"NSUbiquitousKeyValueStore\":[],\"NSUndoManager\":[],\"NSUnit\":[],\"NSUserActivity\":[],\"NSUserDefaults\":[],\"NSUserNotification\":[],\"NSUserScriptTask\":[],\"NSValue\":[],\"NSValueTransformer\":[],\"NSXMLDTD\":[],\"NSXMLDTDNode\":[],\"NSXMLDocument\":[],\"NSXMLElement\":[],\"NSXMLNode\":[],\"NSXMLNodeOptions\":[\"bitflags\"],\"NSXMLParser\":[],\"NSXPCConnection\":[\"bitflags\"],\"NSZone\":[],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"FoundationErrors\",\"FoundationLegacySwiftCompatibility\",\"NSAffineTransform\",\"NSAppleEventDescriptor\",\"NSAppleEventManager\",\"NSAppleScript\",\"NSArchiver\",\"NSArray\",\"NSAttributedString\",\"NSAutoreleasePool\",\"NSBackgroundActivityScheduler\",\"NSBundle\",\"NSByteCountFormatter\",\"NSByteOrder\",\"NSCache\",\"NSCalendar\",\"NSCalendarDate\",\"NSCharacterSet\",\"NSClassDescription\",\"NSCoder\",\"NSComparisonPredicate\",\"NSCompoundPredicate\",\"NSConnection\",\"NSData\",\"NSDate\",\"NSDateComponentsFormatter\",\"NSDateFormatter\",\"NSDateInterval\",\"NSDateIntervalFormatter\",\"NSDebug\",\"NSDecimal\",\"NSDecimalNumber\",\"NSDictionary\",\"NSDistantObject\",\"NSDistributedLock\",\"NSDistributedNotificationCenter\",\"NSEnergyFormatter\",\"NSEnumerator\",\"NSError\",\"NSException\",\"NSExpression\",\"NSExtensionContext\",\"NSExtensionItem\",\"NSExtensionRequestHandling\",\"NSFileCoordinator\",\"NSFileHandle\",\"NSFileManager\",\"NSFilePresenter\",\"NSFileVersion\",\"NSFileWrapper\",\"NSFormatter\",\"NSGarbageCollector\",\"NSGeometry\",\"NSHFSFileTypes\",\"NSHTTPCookie\",\"NSHTTPCookieStorage\",\"NSHashTable\",\"NSHost\",\"NSISO8601DateFormatter\",\"NSIndexPath\",\"NSIndexSet\",\"NSInflectionRule\",\"NSInvocation\",\"NSItemProvider\",\"NSJSONSerialization\",\"NSKeyValueCoding\",\"NSKeyValueObserving\",\"NSKeyValueSharedObservers\",\"NSKeyedArchiver\",\"NSLengthFormatter\",\"NSLinguisticTagger\",\"NSListFormatter\",\"NSLocale\",\"NSLocalizedNumberFormatRule\",\"NSLock\",\"NSMapTable\",\"NSMassFormatter\",\"NSMeasurement\",\"NSMeasurementFormatter\",\"NSMetadata\",\"NSMetadataAttributes\",\"NSMethodSignature\",\"NSMorphology\",\"NSNetServices\",\"NSNotification\",\"NSNotificationQueue\",\"NSNull\",\"NSNumberFormatter\",\"NSObjCRuntime\",\"NSObject\",\"NSObjectScripting\",\"NSOperation\",\"NSOrderedCollectionChange\",\"NSOrderedCollectionDifference\",\"NSOrderedSet\",\"NSOrthography\",\"NSPathUtilities\",\"NSPersonNameComponents\",\"NSPersonNameComponentsFormatter\",\"NSPointerArray\",\"NSPointerFunctions\",\"NSPort\",\"NSPortCoder\",\"NSPortMessage\",\"NSPortNameServer\",\"NSPredicate\",\"NSProcessInfo\",\"NSProgress\",\"NSPropertyList\",\"NSProtocolChecker\",\"NSProxy\",\"NSRange\",\"NSRegularExpression\",\"NSRelativeDateTimeFormatter\",\"NSRunLoop\",\"NSScanner\",\"NSScriptClassDescription\",\"NSScriptCoercionHandler\",\"NSScriptCommand\",\"NSScriptCommandDescription\",\"NSScriptExecutionContext\",\"NSScriptKeyValueCoding\",\"NSScriptObjectSpecifiers\",\"NSScriptStandardSuiteCommands\",\"NSScriptSuiteRegistry\",\"NSScriptWhoseTests\",\"NSSet\",\"NSSortDescriptor\",\"NSSpellServer\",\"NSStream\",\"NSString\",\"NSTask\",\"NSTermOfAddress\",\"NSTextCheckingResult\",\"NSThread\",\"NSTimeZone\",\"NSTimer\",\"NSURL\",\"NSURLAuthenticationChallenge\",\"NSURLCache\",\"NSURLConnection\",\"NSURLCredential\",\"NSURLCredentialStorage\",\"NSURLDownload\",\"NSURLError\",\"NSURLHandle\",\"NSURLProtectionSpace\",\"NSURLProtocol\",\"NSURLRequest\",\"NSURLResponse\",\"NSURLSession\",\"NSUUID\",\"NSUbiquitousKeyValueStore\",\"NSUndoManager\",\"NSUnit\",\"NSUserActivity\",\"NSUserDefaults\",\"NSUserNotification\",\"NSUserScriptTask\",\"NSValue\",\"NSValueTransformer\",\"NSXMLDTD\",\"NSXMLDTDNode\",\"NSXMLDocument\",\"NSXMLElement\",\"NSXMLNode\",\"NSXMLNodeOptions\",\"NSXMLParser\",\"NSXPCConnection\",\"NSZone\",\"bitflags\",\"block2\",\"libc\",\"objc2-core-foundation\"],\"gnustep-1-7\":[\"objc2/gnustep-1-7\",\"block2?/gnustep-1-7\"],\"gnustep-1-8\":[\"gnustep-1-7\",\"objc2/gnustep-1-8\",\"block2?/gnustep-1-8\"],\"gnustep-1-9\":[\"gnustep-1-8\",\"objc2/gnustep-1-9\",\"block2?/gnustep-1-9\"],\"gnustep-2-0\":[\"gnustep-1-9\",\"objc2/gnustep-2-0\",\"block2?/gnustep-2-0\"],\"gnustep-2-1\":[\"gnustep-2-0\",\"objc2/gnustep-2-1\",\"block2?/gnustep-2-1\"],\"libc\":[\"dep:libc\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-core-services\":[\"dep:objc2-core-services\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[],\"unstable-mutation-return-null\":[\"NSNull\"],\"unstable-static-nsstring\":[]}}", + "objc2-io-surface_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CFDictionary\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"NSDictionary\",\"NSObject\",\"NSString\",\"alloc\"],\"name\":\"objc2-foundation\",\"optional\":true,\"req\":\"^0.3.2\"}],\"features\":{\"IOSurface\":[],\"IOSurfaceAPI\":[],\"IOSurfaceBase\":[],\"IOSurfaceRef\":[\"bitflags\"],\"IOSurfaceTypes\":[\"bitflags\"],\"ObjC\":[\"objc2\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"default\":[\"std\",\"IOSurface\",\"IOSurfaceAPI\",\"IOSurfaceBase\",\"IOSurfaceRef\",\"IOSurfaceTypes\",\"bitflags\",\"libc\",\"objc2\",\"objc2-core-foundation\",\"objc2-foundation\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\",\"objc2-core-foundation?/objc2\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-foundation\":[\"dep:objc2-foundation\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-quartz-core_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CFCGTypes\",\"CFDate\",\"objc2\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGColor\",\"CGColorSpace\",\"CGContext\",\"CGPath\",\"objc2\"],\"name\":\"objc2-core-graphics\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CVBase\",\"objc2\"],\"name\":\"objc2-core-video\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"MTLAllocation\",\"MTLDevice\",\"MTLDrawable\",\"MTLPixelFormat\",\"MTLResidencySet\",\"MTLResource\",\"MTLTexture\"],\"name\":\"objc2-metal\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGLTypes\"],\"name\":\"objc2-open-gl\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(target_os = \\\"macos\\\")\"}],\"features\":{\"CAAnimation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"CABase\":[],\"CAConstraintLayoutManager\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CADisplayLink\":[\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSRunLoop\",\"objc2-foundation/NSString\"],\"CAEAGLLayer\":[],\"CAEDRMetadata\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\"],\"CAEmitterCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CAEmitterLayer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CAFrameRateRange\":[],\"CAGradientLayer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"CALayer\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNull\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CAMediaTiming\":[\"objc2-foundation/NSString\"],\"CAMediaTimingFunction\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CAMetalDisplayLink\":[\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSRunLoop\",\"objc2-foundation/NSString\"],\"CAMetalLayer\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\"],\"CAOpenGLLayer\":[\"objc2-foundation/NSObject\"],\"CARemoteLayerClient\":[],\"CARemoteLayerServer\":[],\"CARenderer\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"CAReplicatorLayer\":[\"objc2-foundation/NSObject\"],\"CAScrollLayer\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CAShapeLayer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"CATextLayer\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CATiledLayer\":[\"objc2-foundation/NSObject\"],\"CATransaction\":[\"objc2-foundation/NSString\"],\"CATransform3D\":[\"objc2-foundation/NSValue\"],\"CATransformLayer\":[\"objc2-foundation/NSObject\"],\"CAValueFunction\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"CoreAnimation\":[],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"CAAnimation\",\"CABase\",\"CAConstraintLayoutManager\",\"CADisplayLink\",\"CAEAGLLayer\",\"CAEDRMetadata\",\"CAEmitterCell\",\"CAEmitterLayer\",\"CAFrameRateRange\",\"CAGradientLayer\",\"CALayer\",\"CAMediaTiming\",\"CAMediaTimingFunction\",\"CAMetalDisplayLink\",\"CAMetalLayer\",\"CAOpenGLLayer\",\"CARemoteLayerClient\",\"CARemoteLayerServer\",\"CARenderer\",\"CAReplicatorLayer\",\"CAScrollLayer\",\"CAShapeLayer\",\"CATextLayer\",\"CATiledLayer\",\"CATransaction\",\"CATransform3D\",\"CATransformLayer\",\"CAValueFunction\",\"CoreAnimation\",\"bitflags\",\"block2\",\"libc\",\"objc2-core-foundation\",\"objc2-core-graphics\",\"objc2-core-video\",\"objc2-metal\"],\"gnustep-1-7\":[],\"gnustep-1-8\":[],\"gnustep-1-9\":[],\"gnustep-2-0\":[],\"gnustep-2-1\":[],\"libc\":[\"dep:libc\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-core-graphics\":[\"dep:objc2-core-graphics\"],\"objc2-core-video\":[\"dep:objc2-core-video\"],\"objc2-metal\":[\"dep:objc2-metal\"],\"objc2-open-gl\":[\"dep:objc2-open-gl\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-ui-kit_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CKContainer\",\"CKRecord\",\"CKShare\",\"CKShareMetadata\"],\"name\":\"objc2-cloud-kit\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"NSManagedObjectContext\",\"NSManagedObjectModel\"],\"name\":\"objc2-core-data\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CFCGTypes\",\"CFDate\",\"objc2\"],\"name\":\"objc2-core-foundation\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CGColor\",\"CGContext\",\"CGFont\",\"CGImage\",\"CGPath\",\"objc2\"],\"name\":\"objc2-core-graphics\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CIColor\",\"CIImage\"],\"name\":\"objc2-core-image\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(target_os = \\\"watchos\\\"))\"},{\"default_features\":false,\"features\":[\"CLRegion\"],\"name\":\"objc2-core-location\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CTFont\",\"CTFontDescriptor\",\"objc2\"],\"name\":\"objc2-core-text\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"CADisplayLink\",\"CAFrameRateRange\",\"CALayer\",\"CAMediaTiming\",\"CATransform3D\",\"objc2-core-foundation\"],\"name\":\"objc2-quartz-core\",\"optional\":true,\"req\":\"^0.3.2\",\"target\":\"cfg(not(target_os = \\\"watchos\\\"))\"},{\"default_features\":false,\"features\":[\"NSSymbolEffect\"],\"name\":\"objc2-symbols\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"UTType\"],\"name\":\"objc2-uniform-type-identifiers\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"UNNotificationResponse\"],\"name\":\"objc2-user-notifications\",\"optional\":true,\"req\":\"^0.3.2\"}],\"features\":{\"DocumentManager\":[],\"NSAdaptiveImageGlyph\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSAttributedString\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"NSDataAsset\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSDiffableDataSourceSectionSnapshot\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSFileProviderExtension\":[],\"NSIndexPath_UIKitAdditions\":[\"objc2-foundation/NSIndexPath\"],\"NSItemProvider_UIKitAdditions\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSError\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\"],\"NSLayoutAnchor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSLayoutConstraint\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSLayoutManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSParagraphStyle\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCharacterSet\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSShadow\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSStringDrawing\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSText\":[],\"NSTextAttachment\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSFileWrapper\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextContainer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"NSTextContentManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextElement\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\"],\"NSTextLayoutFragment\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\"],\"NSTextLayoutManager\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"NSTextLineFragment\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextList\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextListElement\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"NSTextRange\":[\"objc2-foundation/NSObjCRuntime\"],\"NSTextSelection\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"NSTextSelectionNavigation\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"NSTextStorage\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"NSTextViewportLayoutController\":[],\"NSToolbar_UIKitAdditions\":[],\"NSTouchBar_UIKitAdditions\":[],\"NSUserActivity_NSItemProvider\":[],\"PrintKitUI\":[],\"ShareSheet\":[],\"UIAccelerometer\":[\"objc2-foundation/NSDate\"],\"UIAccessibility\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"UIAccessibilityAdditions\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\"],\"UIAccessibilityConstants\":[\"bitflags\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"UIAccessibilityContainer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSRange\"],\"UIAccessibilityContentSizeCategoryImageAdjusting\":[],\"UIAccessibilityCustomAction\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\"],\"UIAccessibilityCustomRotor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\"],\"UIAccessibilityElement\":[\"objc2-foundation/NSString\"],\"UIAccessibilityIdentification\":[\"objc2-foundation/NSString\"],\"UIAccessibilityLocationDescriptor\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\"],\"UIAccessibilityZoom\":[],\"UIAction\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIActionSheet\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIActivity\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"UIActivityCollaborationModeRestriction\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIActivityIndicatorView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIActivityItemProvider\":[\"objc2-foundation/NSOperation\",\"objc2-foundation/NSString\"],\"UIActivityItemsConfiguration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\"],\"UIActivityItemsConfigurationReading\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\"],\"UIActivityItemsConfigurationReading_ShareSheet\":[\"objc2-foundation/NSString\"],\"UIActivityViewController\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIAlert\":[],\"UIAlertController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIAlertView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIAppearance\":[\"objc2-foundation/NSArray\"],\"UIApplication\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUserActivity\"],\"UIApplicationShortcutItem\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIAttachmentBehavior\":[\"objc2-foundation/NSArray\"],\"UIBackgroundConfiguration\":[\"objc2-foundation/NSObject\"],\"UIBackgroundExtensionView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIBandSelectionInteraction\":[],\"UIBarAppearance\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIBarButtonItem\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"UIBarButtonItemAppearance\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIBarButtonItemBadge\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIBarButtonItemGroup\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIBarCommon\":[],\"UIBarItem\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIBehavioralStyle\":[],\"UIBezierPath\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIBlurEffect\":[\"objc2-foundation/NSObject\"],\"UIButton\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIButtonConfiguration\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UICalendarSelection\":[],\"UICalendarSelectionMultiDate\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCalendar\"],\"UICalendarSelectionSingleDate\":[\"objc2-foundation/NSCalendar\"],\"UICalendarSelectionWeekOfYear\":[\"objc2-foundation/NSCalendar\"],\"UICalendarView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCalendar\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDateInterval\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTimeZone\"],\"UICalendarViewDecoration\":[],\"UICanvasFeedbackGenerator\":[],\"UICellAccessory\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UICellConfigurationState\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UICloudSharingController\":[\"bitflags\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UICollectionLayoutList\":[\"bitflags\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\"],\"UICollectionView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSProgress\",\"objc2-foundation/NSString\"],\"UICollectionViewCell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UICollectionViewCompositionalLayout\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UICollectionViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UICollectionViewFlowLayout\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\"],\"UICollectionViewItemRegistration\":[\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSString\"],\"UICollectionViewLayout\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UICollectionViewListCell\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UICollectionViewTransitionLayout\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UICollectionViewUpdateItem\":[\"objc2-foundation/NSIndexPath\"],\"UICollisionBehavior\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"UIColor\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIColorPickerViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIColorWell\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UICommand\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIConfigurationColorTransformer\":[],\"UIConfigurationState\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIContentConfiguration\":[\"objc2-foundation/NSObject\"],\"UIContentSizeCategory\":[\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSString\"],\"UIContentSizeCategoryAdjusting\":[],\"UIContentUnavailableButtonProperties\":[\"objc2-foundation/NSObject\"],\"UIContentUnavailableConfiguration\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIContentUnavailableConfigurationState\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIContentUnavailableImageProperties\":[\"objc2-foundation/NSObject\"],\"UIContentUnavailableTextProperties\":[\"objc2-foundation/NSObject\"],\"UIContentUnavailableView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIContextMenuConfiguration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\"],\"UIContextMenuInteraction\":[\"objc2-foundation/NSObject\"],\"UIContextMenuSystem\":[],\"UIContextualAction\":[\"objc2-foundation/NSString\"],\"UIControl\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"UIConversationContext\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSPersonNameComponents\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"UIConversationEntry\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"UICornerConfiguration\":[\"objc2-foundation/NSObject\"],\"UICornerRadius\":[\"objc2-foundation/NSObject\"],\"UIDataDetectors\":[\"bitflags\"],\"UIDataSourceTranslating\":[\"objc2-foundation/NSIndexPath\"],\"UIDatePicker\":[\"objc2-foundation/NSCalendar\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSTimeZone\"],\"UIDeferredMenuElement\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIDevice\":[\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUUID\"],\"UIDiffableDataSource\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOrderedCollectionDifference\",\"objc2-foundation/NSString\"],\"UIDocument\":[\"bitflags\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFilePresenter\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSProgress\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUndoManager\",\"objc2-foundation/NSUserActivity\"],\"UIDocumentBrowserAction\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIDocumentBrowserViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSProgress\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIDocumentInteractionController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIDocumentMenuViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIDocumentPickerExtensionViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIDocumentPickerViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIDocumentProperties\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSURL\"],\"UIDocumentViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIDocumentViewControllerLaunchOptions\":[\"objc2-foundation/NSString\"],\"UIDragInteraction\":[\"objc2-foundation/NSArray\"],\"UIDragItem\":[\"objc2-foundation/NSItemProvider\"],\"UIDragPreview\":[\"objc2-foundation/NSObject\"],\"UIDragPreviewParameters\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSValue\"],\"UIDragSession\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSProgress\",\"objc2-foundation/NSString\"],\"UIDropInteraction\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSProgress\"],\"UIDynamicAnimator\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSString\"],\"UIDynamicBehavior\":[\"objc2-foundation/NSArray\"],\"UIDynamicItemBehavior\":[\"objc2-foundation/NSArray\"],\"UIEditMenuInteraction\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"UIEvent\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSSet\"],\"UIEventAttribution\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIEventAttributionView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIFeedbackGenerator\":[],\"UIFieldBehavior\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\"],\"UIFindInteraction\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"UIFindSession\":[\"objc2-foundation/NSString\"],\"UIFocus\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"UIFocusAnimationCoordinator\":[\"objc2-foundation/NSDate\"],\"UIFocusDebugger\":[],\"UIFocusDefines\":[],\"UIFocusEffect\":[\"objc2-foundation/NSObject\"],\"UIFocusGuide\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"UIFocusMovementHint\":[\"objc2-foundation/NSObject\"],\"UIFocusSystem\":[],\"UIFocusSystem_UIKitAdditions\":[\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIFocusUpdateContext_UIKitAdditions\":[],\"UIFont\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIFontDescriptor\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"UIFontMetrics\":[\"objc2-foundation/NSString\"],\"UIFontPickerViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIFontPickerViewControllerConfiguration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\"],\"UIFoundation\":[],\"UIGeometry\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"UIGestureRecognizer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"UIGestureRecognizerSubclass\":[\"objc2-foundation/NSSet\"],\"UIGlassEffect\":[\"objc2-foundation/NSObject\"],\"UIGraphics\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIGraphicsImageRenderer\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\"],\"UIGraphicsPDFRenderer\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIGraphicsRenderer\":[\"objc2-foundation/NSObject\"],\"UIGraphicsRendererSubclass\":[\"objc2-foundation/NSError\"],\"UIGravityBehavior\":[\"objc2-foundation/NSArray\"],\"UIGuidedAccess\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"UIGuidedAccessRestrictions\":[],\"UIHoverEffect\":[\"objc2-foundation/NSObject\"],\"UIHoverEffectLayer\":[\"objc2-foundation/NSObject\"],\"UIHoverGestureRecognizer\":[\"objc2-foundation/NSCoder\"],\"UIHoverStyle\":[\"objc2-foundation/NSObject\"],\"UIImage\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIImageAsset\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIImageConfiguration\":[\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\"],\"UIImagePickerController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"UIImageReader\":[\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSURL\"],\"UIImageSymbolConfiguration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSLocale\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIImageView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"UIImpactFeedbackGenerator\":[],\"UIIndirectScribbleInteraction\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"UIInputSuggestion\":[],\"UIInputView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIInputViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUUID\"],\"UIInteraction\":[\"objc2-foundation/NSArray\"],\"UIInterface\":[],\"UIKey\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIKeyCommand\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIKeyConstants\":[],\"UIKeyboardLayoutGuide\":[\"objc2-foundation/NSObject\"],\"UIKitCore\":[],\"UIKitDefines\":[],\"UILabel\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UILargeContentViewer\":[\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"UILayoutGuide\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UILetterformAwareAdjusting\":[],\"UILexicon\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIListContentConfiguration\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIListContentImageProperties\":[\"objc2-foundation/NSObject\"],\"UIListContentTextProperties\":[\"objc2-foundation/NSObject\"],\"UIListSeparatorConfiguration\":[\"objc2-foundation/NSObject\"],\"UILocalNotification\":[\"objc2-foundation/NSCalendar\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSTimeZone\"],\"UILocalizedIndexedCollation\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"UILongPressGestureRecognizer\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\"],\"UIMailConversationContext\":[\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"UIMailConversationEntry\":[\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"UIMainMenuSystem\":[\"objc2-foundation/NSObject\"],\"UIManagedDocument\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSFilePresenter\",\"objc2-foundation/NSProgress\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIMenu\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIMenuBuilder\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"UIMenuController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"UIMenuDisplayPreferences\":[\"objc2-foundation/NSObject\"],\"UIMenuElement\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIMenuLeaf\":[\"objc2-foundation/NSString\"],\"UIMenuSystem\":[],\"UIMessageConversationContext\":[],\"UIMessageConversationEntry\":[],\"UIMotionEffect\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UINavigationBar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UINavigationBarAppearance\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UINavigationController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UINavigationItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"UINib\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"UINibDeclarations\":[],\"UINibLoading\":[\"objc2-foundation/NSString\"],\"UINotificationFeedbackGenerator\":[],\"UIOpenURLContext\":[\"objc2-foundation/NSURL\"],\"UIOrientation\":[\"bitflags\"],\"UIPageControl\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIPageControlProgress\":[\"objc2-foundation/NSDate\"],\"UIPageViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIPanGestureRecognizer\":[\"bitflags\",\"objc2-foundation/NSCoder\"],\"UIPasteConfiguration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIPasteConfigurationSupporting\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSItemProvider\"],\"UIPasteControl\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIPasteboard\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIPencilInteraction\":[\"objc2-foundation/NSDate\"],\"UIPickerView\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIPinchGestureRecognizer\":[\"objc2-foundation/NSCoder\"],\"UIPointerAccessory\":[\"objc2-foundation/NSObject\"],\"UIPointerInteraction\":[],\"UIPointerLockState\":[\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"UIPointerRegion\":[\"objc2-foundation/NSObject\"],\"UIPointerStyle\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"UIPopoverBackgroundView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIPopoverController\":[\"objc2-foundation/NSArray\"],\"UIPopoverPresentationController\":[\"objc2-foundation/NSArray\"],\"UIPopoverPresentationControllerSourceItem\":[],\"UIPopoverSupport\":[\"bitflags\"],\"UIPresentationController\":[],\"UIPress\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\"],\"UIPressesEvent\":[\"objc2-foundation/NSSet\"],\"UIPreviewInteraction\":[],\"UIPreviewParameters\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSValue\"],\"UIPrintError\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"UIPrintFormatter\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIPrintInfo\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIPrintInteractionController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIPrintPageRenderer\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSRange\"],\"UIPrintPaper\":[\"objc2-foundation/NSArray\"],\"UIPrintServiceExtension\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSData\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIPrinter\":[\"bitflags\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UIPrinterPickerController\":[\"objc2-foundation/NSError\"],\"UIProgressView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSProgress\"],\"UIPushBehavior\":[\"objc2-foundation/NSArray\"],\"UIReferenceLibraryViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIRefreshControl\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIRegion\":[\"objc2-foundation/NSObject\"],\"UIResponder\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUndoManager\",\"objc2-foundation/NSUserActivity\"],\"UIResponder_UIActivityItemsConfiguration\":[],\"UIRotationGestureRecognizer\":[\"objc2-foundation/NSCoder\"],\"UIScene\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSUserActivity\"],\"UISceneActivationConditions\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSPredicate\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUserActivity\"],\"UISceneConfiguration\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UISceneDefinitions\":[\"objc2-foundation/NSError\",\"objc2-foundation/NSString\"],\"UISceneDestructionCondition\":[\"objc2-foundation/NSObject\"],\"UISceneEnhancedStateRestoration\":[],\"UISceneOptions\":[\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUserActivity\"],\"UISceneSession\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUserActivity\",\"UISceneConfiguration\"],\"UISceneSessionActivationRequest\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUserActivity\"],\"UISceneSizeRestrictions\":[],\"UISceneSystemProtectionManager\":[\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"UISceneWindowingBehaviors\":[],\"UISceneWindowingControlStyle\":[],\"UIScene_AVAudioSession\":[],\"UIScreen\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSString\"],\"UIScreenEdgePanGestureRecognizer\":[\"objc2-foundation/NSCoder\"],\"UIScreenMode\":[],\"UIScreenshotService\":[\"objc2-foundation/NSData\"],\"UIScribbleInteraction\":[],\"UIScrollEdgeElementContainerInteraction\":[],\"UIScrollView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\"],\"UISearchBar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"UISearchContainerViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UISearchController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UISearchDisplayController\":[\"objc2-foundation/NSString\"],\"UISearchSuggestion\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSString\"],\"UISearchTab\":[\"objc2-foundation/NSString\"],\"UISearchTextField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UISegmentedControl\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UISelectionFeedbackGenerator\":[],\"UIShadowProperties\":[\"objc2-foundation/NSObject\"],\"UIShape\":[\"objc2-foundation/NSObject\"],\"UISheetPresentationController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"UISlider\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UISliderTrackConfiguration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UISmartReplySuggestion\":[\"objc2-foundation/NSString\"],\"UISnapBehavior\":[],\"UISplitViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UISplitViewControllerLayoutEnvironment\":[],\"UISpringLoadedInteraction\":[],\"UISpringLoadedInteractionSupporting\":[],\"UIStackView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIStandardTextCursorView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIStateRestoration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSString\"],\"UIStatusBarManager\":[],\"UIStepper\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIStoryboard\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSString\"],\"UIStoryboardPopoverSegue\":[\"objc2-foundation/NSString\"],\"UIStoryboardSegue\":[\"objc2-foundation/NSString\"],\"UIStringDrawing\":[\"objc2-foundation/NSString\"],\"UISwipeActionsConfiguration\":[\"objc2-foundation/NSArray\"],\"UISwipeGestureRecognizer\":[\"bitflags\",\"objc2-foundation/NSCoder\"],\"UISwitch\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UISymbolContentTransition\":[\"objc2-foundation/NSObject\"],\"UISymbolEffectCompletion\":[],\"UITab\":[\"objc2-foundation/NSString\"],\"UITabAccessory\":[],\"UITabBar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UITabBarAppearance\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UITabBarController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSProgress\",\"objc2-foundation/NSString\"],\"UITabBarControllerSidebar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSProgress\"],\"UITabBarItem\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UITabGroup\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"UITabSidebarItem\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"UITableView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSIndexPath\",\"objc2-foundation/NSIndexSet\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSProgress\",\"objc2-foundation/NSString\"],\"UITableViewCell\":[\"bitflags\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UITableViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UITableViewHeaderFooterView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UITapGestureRecognizer\":[\"objc2-foundation/NSCoder\"],\"UITargetedDragPreview\":[\"objc2-foundation/NSObject\"],\"UITargetedPreview\":[\"objc2-foundation/NSObject\"],\"UITextChecker\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"UITextCursorDropPositionAnimator\":[],\"UITextCursorView\":[],\"UITextDragPreviewRenderer\":[\"objc2-foundation/NSRange\"],\"UITextDragURLPreviews\":[\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UITextDragging\":[\"bitflags\",\"objc2-foundation/NSArray\"],\"UITextDropProposal\":[\"objc2-foundation/NSObject\"],\"UITextDropping\":[\"objc2-foundation/NSProgress\"],\"UITextField\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"UITextFormattingCoordinator\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSString\"],\"UITextFormattingViewController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UITextFormattingViewControllerChangeValue\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"UITextFormattingViewControllerComponent\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UITextFormattingViewControllerConfiguration\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"UITextFormattingViewControllerFormattingDescriptor\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"UITextFormattingViewControllerFormattingStyle\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UITextInput\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\"],\"UITextInputContext\":[],\"UITextInputTraits\":[\"bitflags\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UITextInteraction\":[\"objc2-foundation/NSArray\"],\"UITextItem\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UITextItemInteraction\":[],\"UITextLoupeSession\":[],\"UITextPasteConfigurationSupporting\":[],\"UITextPasteDelegate\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSItemProvider\",\"objc2-foundation/NSString\"],\"UITextSearching\":[\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSOrderedSet\",\"objc2-foundation/NSString\"],\"UITextSelectionDisplayInteraction\":[\"objc2-foundation/NSArray\"],\"UITextSelectionHandleView\":[],\"UITextSelectionHighlightView\":[\"objc2-foundation/NSArray\"],\"UITextView\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSValue\"],\"UITimingCurveProvider\":[\"objc2-foundation/NSObject\"],\"UITimingParameters\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"UIToolTipInteraction\":[\"objc2-foundation/NSString\"],\"UIToolbar\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIToolbarAppearance\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UITouch\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSValue\"],\"UITrackingLayoutGuide\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\"],\"UITrait\":[\"objc2-foundation/NSString\"],\"UITraitCollection\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UITraitListEnvironment\":[],\"UIUpdateActionPhase\":[],\"UIUpdateInfo\":[\"objc2-foundation/NSDate\"],\"UIUpdateLink\":[],\"UIUserActivity\":[\"objc2-foundation/NSUserActivity\"],\"UIUserNotificationSettings\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"UIVibrancyEffect\":[\"objc2-foundation/NSObject\"],\"UIVideoEditorController\":[\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIView\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIViewAnimating\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"UIViewConfigurationState\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIViewController\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSBundle\",\"objc2-foundation/NSCoder\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSExtensionContext\",\"objc2-foundation/NSExtensionRequestHandling\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObjCRuntime\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIViewControllerTransition\":[],\"UIViewControllerTransitionCoordinator\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSString\"],\"UIViewControllerTransitioning\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIViewLayoutRegion\":[],\"UIViewPropertyAnimator\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"UIVisualEffect\":[\"objc2-foundation/NSObject\"],\"UIVisualEffectView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\"],\"UIWebView\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSData\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\",\"objc2-foundation/NSURLRequest\"],\"UIWindow\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSNotification\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIWindowScene\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSString\",\"UISceneSizeRestrictions\"],\"UIWindowSceneActivationAction\":[\"objc2-foundation/NSCoder\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UIWindowSceneActivationConfiguration\":[\"objc2-foundation/NSUserActivity\"],\"UIWindowSceneActivationInteraction\":[\"objc2-foundation/NSError\"],\"UIWindowSceneActivationRequestOptions\":[],\"UIWindowSceneDragInteraction\":[],\"UIWindowSceneGeometry\":[\"objc2-foundation/NSObject\"],\"UIWindowSceneGeometryPreferences\":[],\"UIWindowSceneGeometryPreferencesIOS\":[],\"UIWindowSceneGeometryPreferencesMac\":[],\"UIWindowSceneGeometryPreferencesVision\":[],\"UIWindowScenePlacement\":[\"objc2-foundation/NSObject\"],\"UIWindowSceneProminentPlacement\":[\"objc2-foundation/NSObject\"],\"UIWindowScenePushPlacement\":[\"objc2-foundation/NSObject\"],\"UIWindowSceneReplacePlacement\":[\"objc2-foundation/NSObject\"],\"UIWindowSceneStandardPlacement\":[\"objc2-foundation/NSObject\"],\"UIWritingToolsCoordinator\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSString\",\"objc2-foundation/NSUUID\",\"objc2-foundation/NSValue\"],\"UIWritingToolsCoordinatorAnimationParameters\":[],\"UIWritingToolsCoordinatorContext\":[\"objc2-foundation/NSAttributedString\",\"objc2-foundation/NSRange\",\"objc2-foundation/NSUUID\"],\"UIZoomTransitionOptions\":[\"objc2-foundation/NSObject\"],\"UNNotificationResponse_UIKitAdditions\":[],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"DocumentManager\",\"NSAdaptiveImageGlyph\",\"NSAttributedString\",\"NSDataAsset\",\"NSDiffableDataSourceSectionSnapshot\",\"NSFileProviderExtension\",\"NSIndexPath_UIKitAdditions\",\"NSItemProvider_UIKitAdditions\",\"NSLayoutAnchor\",\"NSLayoutConstraint\",\"NSLayoutManager\",\"NSParagraphStyle\",\"NSShadow\",\"NSStringDrawing\",\"NSText\",\"NSTextAttachment\",\"NSTextContainer\",\"NSTextContentManager\",\"NSTextElement\",\"NSTextLayoutFragment\",\"NSTextLayoutManager\",\"NSTextLineFragment\",\"NSTextList\",\"NSTextListElement\",\"NSTextRange\",\"NSTextSelection\",\"NSTextSelectionNavigation\",\"NSTextStorage\",\"NSTextViewportLayoutController\",\"NSToolbar_UIKitAdditions\",\"NSTouchBar_UIKitAdditions\",\"NSUserActivity_NSItemProvider\",\"PrintKitUI\",\"ShareSheet\",\"UIAccelerometer\",\"UIAccessibility\",\"UIAccessibilityAdditions\",\"UIAccessibilityConstants\",\"UIAccessibilityContainer\",\"UIAccessibilityContentSizeCategoryImageAdjusting\",\"UIAccessibilityCustomAction\",\"UIAccessibilityCustomRotor\",\"UIAccessibilityElement\",\"UIAccessibilityIdentification\",\"UIAccessibilityLocationDescriptor\",\"UIAccessibilityZoom\",\"UIAction\",\"UIActionSheet\",\"UIActivity\",\"UIActivityCollaborationModeRestriction\",\"UIActivityIndicatorView\",\"UIActivityItemProvider\",\"UIActivityItemsConfiguration\",\"UIActivityItemsConfigurationReading\",\"UIActivityItemsConfigurationReading_ShareSheet\",\"UIActivityViewController\",\"UIAlert\",\"UIAlertController\",\"UIAlertView\",\"UIAppearance\",\"UIApplication\",\"UIApplicationShortcutItem\",\"UIAttachmentBehavior\",\"UIBackgroundConfiguration\",\"UIBackgroundExtensionView\",\"UIBandSelectionInteraction\",\"UIBarAppearance\",\"UIBarButtonItem\",\"UIBarButtonItemAppearance\",\"UIBarButtonItemBadge\",\"UIBarButtonItemGroup\",\"UIBarCommon\",\"UIBarItem\",\"UIBehavioralStyle\",\"UIBezierPath\",\"UIBlurEffect\",\"UIButton\",\"UIButtonConfiguration\",\"UICalendarSelection\",\"UICalendarSelectionMultiDate\",\"UICalendarSelectionSingleDate\",\"UICalendarSelectionWeekOfYear\",\"UICalendarView\",\"UICalendarViewDecoration\",\"UICanvasFeedbackGenerator\",\"UICellAccessory\",\"UICellConfigurationState\",\"UICloudSharingController\",\"UICollectionLayoutList\",\"UICollectionView\",\"UICollectionViewCell\",\"UICollectionViewCompositionalLayout\",\"UICollectionViewController\",\"UICollectionViewFlowLayout\",\"UICollectionViewItemRegistration\",\"UICollectionViewLayout\",\"UICollectionViewListCell\",\"UICollectionViewTransitionLayout\",\"UICollectionViewUpdateItem\",\"UICollisionBehavior\",\"UIColor\",\"UIColorPickerViewController\",\"UIColorWell\",\"UICommand\",\"UIConfigurationColorTransformer\",\"UIConfigurationState\",\"UIContentConfiguration\",\"UIContentSizeCategory\",\"UIContentSizeCategoryAdjusting\",\"UIContentUnavailableButtonProperties\",\"UIContentUnavailableConfiguration\",\"UIContentUnavailableConfigurationState\",\"UIContentUnavailableImageProperties\",\"UIContentUnavailableTextProperties\",\"UIContentUnavailableView\",\"UIContextMenuConfiguration\",\"UIContextMenuInteraction\",\"UIContextMenuSystem\",\"UIContextualAction\",\"UIControl\",\"UIConversationContext\",\"UIConversationEntry\",\"UICornerConfiguration\",\"UICornerRadius\",\"UIDataDetectors\",\"UIDataSourceTranslating\",\"UIDatePicker\",\"UIDeferredMenuElement\",\"UIDevice\",\"UIDiffableDataSource\",\"UIDocument\",\"UIDocumentBrowserAction\",\"UIDocumentBrowserViewController\",\"UIDocumentInteractionController\",\"UIDocumentMenuViewController\",\"UIDocumentPickerExtensionViewController\",\"UIDocumentPickerViewController\",\"UIDocumentProperties\",\"UIDocumentViewController\",\"UIDocumentViewControllerLaunchOptions\",\"UIDragInteraction\",\"UIDragItem\",\"UIDragPreview\",\"UIDragPreviewParameters\",\"UIDragSession\",\"UIDropInteraction\",\"UIDynamicAnimator\",\"UIDynamicBehavior\",\"UIDynamicItemBehavior\",\"UIEditMenuInteraction\",\"UIEvent\",\"UIEventAttribution\",\"UIEventAttributionView\",\"UIFeedbackGenerator\",\"UIFieldBehavior\",\"UIFindInteraction\",\"UIFindSession\",\"UIFocus\",\"UIFocusAnimationCoordinator\",\"UIFocusDebugger\",\"UIFocusDefines\",\"UIFocusEffect\",\"UIFocusGuide\",\"UIFocusMovementHint\",\"UIFocusSystem\",\"UIFocusSystem_UIKitAdditions\",\"UIFocusUpdateContext_UIKitAdditions\",\"UIFont\",\"UIFontDescriptor\",\"UIFontMetrics\",\"UIFontPickerViewController\",\"UIFontPickerViewControllerConfiguration\",\"UIFoundation\",\"UIGeometry\",\"UIGestureRecognizer\",\"UIGestureRecognizerSubclass\",\"UIGlassEffect\",\"UIGraphics\",\"UIGraphicsImageRenderer\",\"UIGraphicsPDFRenderer\",\"UIGraphicsRenderer\",\"UIGraphicsRendererSubclass\",\"UIGravityBehavior\",\"UIGuidedAccess\",\"UIGuidedAccessRestrictions\",\"UIHoverEffect\",\"UIHoverEffectLayer\",\"UIHoverGestureRecognizer\",\"UIHoverStyle\",\"UIImage\",\"UIImageAsset\",\"UIImageConfiguration\",\"UIImagePickerController\",\"UIImageReader\",\"UIImageSymbolConfiguration\",\"UIImageView\",\"UIImpactFeedbackGenerator\",\"UIIndirectScribbleInteraction\",\"UIInputSuggestion\",\"UIInputView\",\"UIInputViewController\",\"UIInteraction\",\"UIInterface\",\"UIKey\",\"UIKeyCommand\",\"UIKeyConstants\",\"UIKeyboardLayoutGuide\",\"UIKitCore\",\"UIKitDefines\",\"UILabel\",\"UILargeContentViewer\",\"UILayoutGuide\",\"UILetterformAwareAdjusting\",\"UILexicon\",\"UIListContentConfiguration\",\"UIListContentImageProperties\",\"UIListContentTextProperties\",\"UIListSeparatorConfiguration\",\"UILocalNotification\",\"UILocalizedIndexedCollation\",\"UILongPressGestureRecognizer\",\"UIMailConversationContext\",\"UIMailConversationEntry\",\"UIMainMenuSystem\",\"UIManagedDocument\",\"UIMenu\",\"UIMenuBuilder\",\"UIMenuController\",\"UIMenuDisplayPreferences\",\"UIMenuElement\",\"UIMenuLeaf\",\"UIMenuSystem\",\"UIMessageConversationContext\",\"UIMessageConversationEntry\",\"UIMotionEffect\",\"UINavigationBar\",\"UINavigationBarAppearance\",\"UINavigationController\",\"UINavigationItem\",\"UINib\",\"UINibDeclarations\",\"UINibLoading\",\"UINotificationFeedbackGenerator\",\"UIOpenURLContext\",\"UIOrientation\",\"UIPageControl\",\"UIPageControlProgress\",\"UIPageViewController\",\"UIPanGestureRecognizer\",\"UIPasteConfiguration\",\"UIPasteConfigurationSupporting\",\"UIPasteControl\",\"UIPasteboard\",\"UIPencilInteraction\",\"UIPickerView\",\"UIPinchGestureRecognizer\",\"UIPointerAccessory\",\"UIPointerInteraction\",\"UIPointerLockState\",\"UIPointerRegion\",\"UIPointerStyle\",\"UIPopoverBackgroundView\",\"UIPopoverController\",\"UIPopoverPresentationController\",\"UIPopoverPresentationControllerSourceItem\",\"UIPopoverSupport\",\"UIPresentationController\",\"UIPress\",\"UIPressesEvent\",\"UIPreviewInteraction\",\"UIPreviewParameters\",\"UIPrintError\",\"UIPrintFormatter\",\"UIPrintInfo\",\"UIPrintInteractionController\",\"UIPrintPageRenderer\",\"UIPrintPaper\",\"UIPrintServiceExtension\",\"UIPrinter\",\"UIPrinterPickerController\",\"UIProgressView\",\"UIPushBehavior\",\"UIReferenceLibraryViewController\",\"UIRefreshControl\",\"UIRegion\",\"UIResponder\",\"UIResponder_UIActivityItemsConfiguration\",\"UIRotationGestureRecognizer\",\"UIScene\",\"UISceneActivationConditions\",\"UISceneConfiguration\",\"UISceneDefinitions\",\"UISceneDestructionCondition\",\"UISceneEnhancedStateRestoration\",\"UISceneOptions\",\"UISceneSession\",\"UISceneSessionActivationRequest\",\"UISceneSizeRestrictions\",\"UISceneSystemProtectionManager\",\"UISceneWindowingBehaviors\",\"UISceneWindowingControlStyle\",\"UIScene_AVAudioSession\",\"UIScreen\",\"UIScreenEdgePanGestureRecognizer\",\"UIScreenMode\",\"UIScreenshotService\",\"UIScribbleInteraction\",\"UIScrollEdgeElementContainerInteraction\",\"UIScrollView\",\"UISearchBar\",\"UISearchContainerViewController\",\"UISearchController\",\"UISearchDisplayController\",\"UISearchSuggestion\",\"UISearchTab\",\"UISearchTextField\",\"UISegmentedControl\",\"UISelectionFeedbackGenerator\",\"UIShadowProperties\",\"UIShape\",\"UISheetPresentationController\",\"UISlider\",\"UISliderTrackConfiguration\",\"UISmartReplySuggestion\",\"UISnapBehavior\",\"UISplitViewController\",\"UISplitViewControllerLayoutEnvironment\",\"UISpringLoadedInteraction\",\"UISpringLoadedInteractionSupporting\",\"UIStackView\",\"UIStandardTextCursorView\",\"UIStateRestoration\",\"UIStatusBarManager\",\"UIStepper\",\"UIStoryboard\",\"UIStoryboardPopoverSegue\",\"UIStoryboardSegue\",\"UIStringDrawing\",\"UISwipeActionsConfiguration\",\"UISwipeGestureRecognizer\",\"UISwitch\",\"UISymbolContentTransition\",\"UISymbolEffectCompletion\",\"UITab\",\"UITabAccessory\",\"UITabBar\",\"UITabBarAppearance\",\"UITabBarController\",\"UITabBarControllerSidebar\",\"UITabBarItem\",\"UITabGroup\",\"UITabSidebarItem\",\"UITableView\",\"UITableViewCell\",\"UITableViewController\",\"UITableViewHeaderFooterView\",\"UITapGestureRecognizer\",\"UITargetedDragPreview\",\"UITargetedPreview\",\"UITextChecker\",\"UITextCursorDropPositionAnimator\",\"UITextCursorView\",\"UITextDragPreviewRenderer\",\"UITextDragURLPreviews\",\"UITextDragging\",\"UITextDropProposal\",\"UITextDropping\",\"UITextField\",\"UITextFormattingCoordinator\",\"UITextFormattingViewController\",\"UITextFormattingViewControllerChangeValue\",\"UITextFormattingViewControllerComponent\",\"UITextFormattingViewControllerConfiguration\",\"UITextFormattingViewControllerFormattingDescriptor\",\"UITextFormattingViewControllerFormattingStyle\",\"UITextInput\",\"UITextInputContext\",\"UITextInputTraits\",\"UITextInteraction\",\"UITextItem\",\"UITextItemInteraction\",\"UITextLoupeSession\",\"UITextPasteConfigurationSupporting\",\"UITextPasteDelegate\",\"UITextSearching\",\"UITextSelectionDisplayInteraction\",\"UITextSelectionHandleView\",\"UITextSelectionHighlightView\",\"UITextView\",\"UITimingCurveProvider\",\"UITimingParameters\",\"UIToolTipInteraction\",\"UIToolbar\",\"UIToolbarAppearance\",\"UITouch\",\"UITrackingLayoutGuide\",\"UITrait\",\"UITraitCollection\",\"UITraitListEnvironment\",\"UIUpdateActionPhase\",\"UIUpdateInfo\",\"UIUpdateLink\",\"UIUserActivity\",\"UIUserNotificationSettings\",\"UIVibrancyEffect\",\"UIVideoEditorController\",\"UIView\",\"UIViewAnimating\",\"UIViewConfigurationState\",\"UIViewController\",\"UIViewControllerTransition\",\"UIViewControllerTransitionCoordinator\",\"UIViewControllerTransitioning\",\"UIViewLayoutRegion\",\"UIViewPropertyAnimator\",\"UIVisualEffect\",\"UIVisualEffectView\",\"UIWebView\",\"UIWindow\",\"UIWindowScene\",\"UIWindowSceneActivationAction\",\"UIWindowSceneActivationConfiguration\",\"UIWindowSceneActivationInteraction\",\"UIWindowSceneActivationRequestOptions\",\"UIWindowSceneDragInteraction\",\"UIWindowSceneGeometry\",\"UIWindowSceneGeometryPreferences\",\"UIWindowSceneGeometryPreferencesIOS\",\"UIWindowSceneGeometryPreferencesMac\",\"UIWindowSceneGeometryPreferencesVision\",\"UIWindowScenePlacement\",\"UIWindowSceneProminentPlacement\",\"UIWindowScenePushPlacement\",\"UIWindowSceneReplacePlacement\",\"UIWindowSceneStandardPlacement\",\"UIWritingToolsCoordinator\",\"UIWritingToolsCoordinatorAnimationParameters\",\"UIWritingToolsCoordinatorContext\",\"UIZoomTransitionOptions\",\"UNNotificationResponse_UIKitAdditions\",\"bitflags\",\"block2\",\"objc2-cloud-kit\",\"objc2-core-data\",\"objc2-core-foundation\",\"objc2-core-graphics\",\"objc2-core-image\",\"objc2-core-location\",\"objc2-core-text\",\"objc2-quartz-core\",\"objc2-user-notifications\"],\"objc2-cloud-kit\":[\"dep:objc2-cloud-kit\"],\"objc2-core-data\":[\"dep:objc2-core-data\"],\"objc2-core-foundation\":[\"dep:objc2-core-foundation\"],\"objc2-core-graphics\":[\"dep:objc2-core-graphics\"],\"objc2-core-image\":[\"dep:objc2-core-image\"],\"objc2-core-location\":[\"dep:objc2-core-location\"],\"objc2-core-text\":[\"dep:objc2-core-text\"],\"objc2-quartz-core\":[\"dep:objc2-quartz-core\"],\"objc2-symbols\":[\"dep:objc2-symbols\"],\"objc2-uniform-type-identifiers\":[\"dep:objc2-uniform-type-identifiers\"],\"objc2-user-notifications\":[\"dep:objc2-user-notifications\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2-user-notifications_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"req\":\">=0.6.2, <0.8.0\"},{\"default_features\":false,\"features\":[\"CLRegion\"],\"name\":\"objc2-core-location\",\"optional\":true,\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3.2\"}],\"features\":{\"NSString_UserNotifications\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSString\"],\"UNError\":[\"objc2-foundation/NSString\"],\"UNNotification\":[\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"UNNotificationAction\":[\"bitflags\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UNNotificationActionIcon\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UNNotificationAttachment\":[\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSURL\"],\"UNNotificationAttributedMessageContext\":[],\"UNNotificationCategory\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UNNotificationContent\":[\"objc2-foundation/NSArray\",\"objc2-foundation/NSDictionary\",\"objc2-foundation/NSError\",\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\",\"objc2-foundation/NSValue\"],\"UNNotificationRequest\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UNNotificationResponse\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UNNotificationServiceExtension\":[],\"UNNotificationSettings\":[\"objc2-foundation/NSObject\"],\"UNNotificationSound\":[\"objc2-foundation/NSObject\",\"objc2-foundation/NSString\"],\"UNNotificationTrigger\":[\"objc2-foundation/NSCalendar\",\"objc2-foundation/NSDate\",\"objc2-foundation/NSObject\"],\"UNUserNotificationCenter\":[\"bitflags\",\"objc2-foundation/NSArray\",\"objc2-foundation/NSError\",\"objc2-foundation/NSSet\",\"objc2-foundation/NSString\"],\"alloc\":[],\"bitflags\":[\"dep:bitflags\"],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"NSString_UserNotifications\",\"UNError\",\"UNNotification\",\"UNNotificationAction\",\"UNNotificationActionIcon\",\"UNNotificationAttachment\",\"UNNotificationAttributedMessageContext\",\"UNNotificationCategory\",\"UNNotificationContent\",\"UNNotificationRequest\",\"UNNotificationResponse\",\"UNNotificationServiceExtension\",\"UNNotificationSettings\",\"UNNotificationSound\",\"UNNotificationTrigger\",\"UNUserNotificationCenter\",\"bitflags\",\"block2\",\"objc2-core-location\"],\"objc2-core-location\":[\"dep:objc2-core-location\"],\"std\":[\"alloc\"],\"unstable-darwin-objc\":[]}}", + "objc2_0.6.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"backtrace\",\"req\":\"^0.3.74\"},{\"kind\":\"dev\",\"name\":\"core-foundation\",\"req\":\"^0.10.0\",\"target\":\"cfg(target_vendor = \\\"apple\\\")\"},{\"kind\":\"dev\",\"name\":\"iai\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.158\"},{\"kind\":\"dev\",\"name\":\"memoffset\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"name\":\"objc2-encode\",\"req\":\"^4.1.0\"},{\"default_features\":false,\"name\":\"objc2-exception-helper\",\"optional\":true,\"req\":\"^0.1.1\"},{\"name\":\"objc2-proc-macros\",\"optional\":true,\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"alloc\":[\"objc2-encode/alloc\"],\"catch-all\":[\"exception\"],\"default\":[\"std\"],\"disable-encoding-assertions\":[],\"exception\":[\"dep:objc2-exception-helper\"],\"gnustep-1-7\":[\"unstable-static-class\",\"objc2-exception-helper?/gnustep-1-7\"],\"gnustep-1-8\":[\"gnustep-1-7\",\"objc2-exception-helper?/gnustep-1-8\"],\"gnustep-1-9\":[\"gnustep-1-8\",\"objc2-exception-helper?/gnustep-1-9\"],\"gnustep-2-0\":[\"gnustep-1-9\",\"objc2-exception-helper?/gnustep-2-0\"],\"gnustep-2-1\":[\"gnustep-2-0\",\"objc2-exception-helper?/gnustep-2-1\"],\"objc2-proc-macros\":[],\"relax-sign-encoding\":[],\"relax-void-encoding\":[],\"std\":[\"alloc\",\"objc2-encode/std\"],\"unstable-apple-new\":[],\"unstable-arbitrary-self-types\":[],\"unstable-autoreleasesafe\":[],\"unstable-coerce-pointee\":[],\"unstable-compiler-rt\":[\"gnustep-1-7\"],\"unstable-gnustep-strict-apple-compat\":[\"gnustep-1-7\"],\"unstable-objfw\":[],\"unstable-requires-macos\":[],\"unstable-static-class\":[\"dep:objc2-proc-macros\"],\"unstable-static-class-inlined\":[\"unstable-static-class\"],\"unstable-static-sel\":[\"dep:objc2-proc-macros\"],\"unstable-static-sel-inlined\":[\"unstable-static-sel\"],\"unstable-winobjc\":[\"gnustep-1-8\"],\"verify\":[]}}", + "object_0.37.3": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"crc32fast\",\"optional\":true,\"req\":\"^1.2\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"default-hasher\"],\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15.0\"},{\"default_features\":false,\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2.4.1\"},{\"name\":\"ruzstd\",\"optional\":true,\"req\":\"^0.8.1\"},{\"default_features\":false,\"name\":\"wasmparser\",\"optional\":true,\"req\":\"^0.236.0\"}],\"features\":{\"all\":[\"read\",\"write\",\"build\",\"std\",\"compression\",\"wasm\"],\"archive\":[],\"build\":[\"build_core\",\"write_std\",\"elf\"],\"build_core\":[\"read_core\",\"write_core\"],\"cargo-all\":[],\"coff\":[],\"compression\":[\"dep:flate2\",\"dep:ruzstd\",\"std\"],\"default\":[\"read\",\"compression\"],\"doc\":[\"read_core\",\"write_std\",\"build_core\",\"std\",\"compression\",\"archive\",\"coff\",\"elf\",\"macho\",\"pe\",\"wasm\",\"xcoff\"],\"elf\":[],\"macho\":[],\"pe\":[\"coff\"],\"read\":[\"read_core\",\"archive\",\"coff\",\"elf\",\"macho\",\"pe\",\"xcoff\",\"unaligned\"],\"read_core\":[],\"rustc-dep-of-std\":[\"core\",\"alloc\",\"memchr/rustc-dep-of-std\"],\"std\":[\"memchr/std\"],\"unaligned\":[],\"unstable\":[],\"unstable-all\":[\"all\",\"unstable\"],\"wasm\":[\"dep:wasmparser\"],\"write\":[\"write_std\",\"coff\",\"elf\",\"macho\",\"pe\",\"xcoff\"],\"write_core\":[\"dep:crc32fast\",\"dep:indexmap\",\"dep:hashbrown\"],\"write_std\":[\"write_core\",\"std\",\"indexmap?/std\",\"crc32fast?/std\"],\"xcoff\":[]}}", "once_cell_1.21.3": "{\"dependencies\":[{\"name\":\"critical-section\",\"optional\":true,\"req\":\"^1.1.3\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"critical-section\",\"req\":\"^1.1.3\"},{\"default_features\":false,\"name\":\"parking_lot_core\",\"optional\":true,\"req\":\"^0.9.10\"},{\"default_features\":false,\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.10.6\"}],\"features\":{\"alloc\":[\"race\"],\"atomic-polyfill\":[\"critical-section\"],\"critical-section\":[\"dep:critical-section\",\"portable-atomic\"],\"default\":[\"std\"],\"parking_lot\":[\"dep:parking_lot_core\"],\"portable-atomic\":[\"dep:portable-atomic\"],\"race\":[],\"std\":[\"alloc\"],\"unstable\":[]}}", - "once_cell_polyfill_1.70.1": "{\"dependencies\":[],\"features\":{\"default\":[]}}", + "once_cell_polyfill_1.70.2": "{\"dependencies\":[],\"features\":{\"default\":[]}}", + "opaque-debug_0.3.1": "{\"dependencies\":[],\"features\":{}}", "openssl-macros_0.1.1": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", "openssl-probe_0.1.6": "{\"dependencies\":[],\"features\":{}}", - "openssl-src_300.5.1+3.5.1": "{\"dependencies\":[{\"name\":\"cc\",\"req\":\"^1.0.79\"}],\"features\":{\"camellia\":[],\"default\":[],\"force-engine\":[],\"idea\":[],\"ktls\":[],\"legacy\":[],\"no-dso\":[],\"seed\":[],\"ssl3\":[],\"weak-crypto\":[]}}", + "openssl-probe_0.2.1": "{\"dependencies\":[],\"features\":{}}", + "openssl-src_300.5.5+3.5.5": "{\"dependencies\":[{\"name\":\"cc\",\"req\":\"^1.0.79\"}],\"features\":{\"camellia\":[],\"default\":[],\"force-engine\":[],\"idea\":[],\"ktls\":[],\"legacy\":[],\"no-dso\":[],\"seed\":[],\"ssl3\":[],\"weak-crypto\":[]}}", "openssl-sys_0.9.111": "{\"dependencies\":[{\"features\":[\"ssl\",\"bindgen\"],\"name\":\"aws-lc-fips-sys\",\"optional\":true,\"req\":\"^0.13\"},{\"features\":[\"ssl\"],\"name\":\"aws-lc-sys\",\"optional\":true,\"req\":\"^0.27\"},{\"features\":[\"experimental\"],\"kind\":\"build\",\"name\":\"bindgen\",\"optional\":true,\"req\":\"^0.72.0\"},{\"name\":\"bssl-sys\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.61\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"features\":[\"legacy\"],\"kind\":\"build\",\"name\":\"openssl-src\",\"optional\":true,\"req\":\"^300.2.0\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.9\"},{\"kind\":\"build\",\"name\":\"vcpkg\",\"req\":\"^0.2.8\"}],\"features\":{\"aws-lc\":[\"dep:aws-lc-sys\"],\"aws-lc-fips\":[\"dep:aws-lc-fips-sys\"],\"unstable_boringssl\":[\"bssl-sys\"],\"vendored\":[\"openssl-src\"]}}", - "openssl_0.10.73": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.2.1\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"ffi\",\"package\":\"openssl-sys\",\"req\":\"^0.9.109\"},{\"name\":\"foreign-types\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"once_cell\",\"req\":\"^1.5.2\"},{\"name\":\"openssl-macros\",\"req\":\"^0.1.1\"}],\"features\":{\"aws-lc\":[\"ffi/aws-lc\"],\"bindgen\":[\"ffi/bindgen\"],\"default\":[],\"unstable_boringssl\":[\"ffi/unstable_boringssl\"],\"v101\":[],\"v102\":[],\"v110\":[],\"v111\":[],\"vendored\":[\"ffi/vendored\"]}}", + "openssl_0.10.75": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.2.1\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"ffi\",\"package\":\"openssl-sys\",\"req\":\"^0.9.111\"},{\"name\":\"foreign-types\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"once_cell\",\"req\":\"^1.5.2\"},{\"name\":\"openssl-macros\",\"req\":\"^0.1.1\"}],\"features\":{\"aws-lc\":[\"ffi/aws-lc\"],\"aws-lc-fips\":[\"ffi/aws-lc-fips\"],\"bindgen\":[\"ffi/bindgen\"],\"default\":[],\"unstable_boringssl\":[\"ffi/unstable_boringssl\"],\"v101\":[],\"v102\":[],\"v110\":[],\"v111\":[],\"vendored\":[\"ffi/vendored\"]}}", "opentelemetry-appender-tracing_0.31.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.21\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.21\"},{\"default_features\":false,\"features\":[\"logs\"],\"name\":\"opentelemetry\",\"req\":\"^0.31\"},{\"default_features\":false,\"features\":[\"logs\"],\"kind\":\"dev\",\"name\":\"opentelemetry-stdout\",\"req\":\"^0.31\"},{\"default_features\":false,\"features\":[\"logs\",\"testing\",\"internal-logs\"],\"kind\":\"dev\",\"name\":\"opentelemetry_sdk\",\"req\":\"^0.31\"},{\"features\":[\"flamegraph\",\"criterion\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.14\",\"target\":\"cfg(not(target_os = \\\"windows\\\"))\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\">=0.1.40\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\">=0.1.40\"},{\"default_features\":false,\"name\":\"tracing-core\",\"req\":\">=0.1.33\"},{\"name\":\"tracing-log\",\"optional\":true,\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"tracing-log\",\"req\":\"^0.2\"},{\"name\":\"tracing-opentelemetry\",\"optional\":true,\"req\":\"^0.32\"},{\"default_features\":false,\"features\":[\"registry\",\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"env-filter\",\"registry\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"}],\"features\":{\"default\":[],\"experimental_metadata_attributes\":[\"dep:tracing-log\"],\"experimental_use_tracing_span_context\":[\"tracing-opentelemetry\"],\"spec_unstable_logs_enabled\":[\"opentelemetry/spec_unstable_logs_enabled\"]}}", "opentelemetry-http_0.31.0": "{\"dependencies\":[{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"http\",\"req\":\"^1.1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"hyper\",\"optional\":true,\"req\":\"^1.3\"},{\"features\":[\"client-legacy\",\"http1\",\"http2\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"trace\"],\"name\":\"opentelemetry\",\"req\":\"^0.31\"},{\"default_features\":false,\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"default_features\":false,\"features\":[\"time\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"default\":[\"internal-logs\"],\"hyper\":[\"dep:http-body-util\",\"dep:hyper\",\"dep:hyper-util\",\"dep:tokio\"],\"internal-logs\":[\"opentelemetry/internal-logs\"],\"reqwest\":[\"dep:reqwest\"],\"reqwest-blocking\":[\"dep:reqwest\",\"reqwest/blocking\"],\"reqwest-rustls\":[\"dep:reqwest\",\"reqwest/rustls-tls-native-roots\"],\"reqwest-rustls-webpki-roots\":[\"dep:reqwest\",\"reqwest/rustls-tls-webpki-roots\"]}}", "opentelemetry-otlp_0.31.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.1.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"http\",\"optional\":true,\"req\":\"^1.1\"},{\"default_features\":false,\"name\":\"opentelemetry\",\"req\":\"^0.31\"},{\"default_features\":false,\"name\":\"opentelemetry-http\",\"optional\":true,\"req\":\"^0.31\"},{\"default_features\":false,\"name\":\"opentelemetry-proto\",\"req\":\"^0.31\"},{\"default_features\":false,\"name\":\"opentelemetry_sdk\",\"req\":\"^0.31\"},{\"default_features\":false,\"features\":[\"trace\",\"testing\"],\"kind\":\"dev\",\"name\":\"opentelemetry_sdk\",\"req\":\"^0.31\"},{\"name\":\"prost\",\"optional\":true,\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"temp-env\",\"req\":\"^0.3.6\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"sync\",\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"net\"],\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"tonic\",\"optional\":true,\"req\":\"^0.14.1\"},{\"default_features\":false,\"features\":[\"router\",\"server\"],\"kind\":\"dev\",\"name\":\"tonic\",\"req\":\"^0.14.1\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\">=0.1.40\"},{\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.13\"}],\"features\":{\"default\":[\"http-proto\",\"reqwest-blocking-client\",\"trace\",\"metrics\",\"logs\",\"internal-logs\"],\"grpc-tonic\":[\"tonic\",\"prost\",\"http\",\"tokio\",\"opentelemetry-proto/gen-tonic\"],\"gzip-http\":[\"flate2\"],\"gzip-tonic\":[\"tonic/gzip\"],\"http-json\":[\"serde_json\",\"prost\",\"opentelemetry-http\",\"opentelemetry-proto/gen-tonic-messages\",\"opentelemetry-proto/with-serde\",\"http\",\"trace\",\"metrics\"],\"http-proto\":[\"prost\",\"opentelemetry-http\",\"opentelemetry-proto/gen-tonic-messages\",\"http\",\"trace\",\"metrics\"],\"hyper-client\":[\"opentelemetry-http/hyper\"],\"integration-testing\":[\"tonic\",\"prost\",\"tokio/full\",\"trace\",\"logs\"],\"internal-logs\":[\"tracing\",\"opentelemetry_sdk/internal-logs\",\"opentelemetry-http/internal-logs\"],\"logs\":[\"opentelemetry/logs\",\"opentelemetry_sdk/logs\",\"opentelemetry-proto/logs\"],\"metrics\":[\"opentelemetry/metrics\",\"opentelemetry_sdk/metrics\",\"opentelemetry-proto/metrics\"],\"reqwest-blocking-client\":[\"reqwest/blocking\",\"opentelemetry-http/reqwest-blocking\"],\"reqwest-client\":[\"reqwest\",\"opentelemetry-http/reqwest\"],\"reqwest-rustls\":[\"reqwest\",\"opentelemetry-http/reqwest-rustls\"],\"reqwest-rustls-webpki-roots\":[\"reqwest\",\"opentelemetry-http/reqwest-rustls-webpki-roots\"],\"serialize\":[\"serde\",\"serde_json\"],\"tls\":[\"tonic/tls-ring\"],\"tls-roots\":[\"tls\",\"tonic/tls-native-roots\"],\"tls-webpki-roots\":[\"tls\",\"tonic/tls-webpki-roots\"],\"trace\":[\"opentelemetry/trace\",\"opentelemetry_sdk/trace\",\"opentelemetry-proto/trace\"],\"zstd-http\":[\"zstd\"],\"zstd-tonic\":[\"tonic/zstd\"]}}", @@ -967,20 +1007,22 @@ "opentelemetry_sdk_0.31.0": "{\"dependencies\":[{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"futures-channel\",\"req\":\"^0.3\"},{\"name\":\"futures-executor\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\",\"sink\",\"async-await-macro\"],\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"http\",\"optional\":true,\"req\":\"^1.1\"},{\"default_features\":false,\"name\":\"opentelemetry\",\"req\":\"^0.31\"},{\"default_features\":false,\"name\":\"opentelemetry-http\",\"optional\":true,\"req\":\"^0.31\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.0\"},{\"features\":[\"flamegraph\",\"criterion\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.14\",\"target\":\"cfg(not(target_os = \\\"windows\\\"))\"},{\"default_features\":false,\"features\":[\"std\",\"std_rng\",\"small_rng\",\"os_rng\",\"thread_rng\"],\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.23.0\"},{\"default_features\":false,\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"temp-env\",\"req\":\"^0.3.6\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"url\",\"optional\":true,\"req\":\"^2.5\"}],\"features\":{\"default\":[\"trace\",\"metrics\",\"logs\",\"internal-logs\"],\"experimental_async_runtime\":[],\"experimental_logs_batch_log_processor_with_async_runtime\":[\"logs\",\"experimental_async_runtime\"],\"experimental_logs_concurrent_log_processor\":[\"logs\"],\"experimental_metrics_custom_reader\":[\"metrics\"],\"experimental_metrics_disable_name_validation\":[\"metrics\"],\"experimental_metrics_periodicreader_with_async_runtime\":[\"metrics\",\"experimental_async_runtime\"],\"experimental_trace_batch_span_processor_with_async_runtime\":[\"tokio/sync\",\"trace\",\"experimental_async_runtime\"],\"internal-logs\":[\"opentelemetry/internal-logs\"],\"jaeger_remote_sampler\":[\"trace\",\"opentelemetry-http\",\"http\",\"serde\",\"serde_json\",\"url\",\"experimental_async_runtime\"],\"logs\":[\"opentelemetry/logs\"],\"metrics\":[\"opentelemetry/metrics\"],\"rt-tokio\":[\"tokio/rt\",\"tokio/time\",\"tokio-stream\",\"experimental_async_runtime\"],\"rt-tokio-current-thread\":[\"tokio/rt\",\"tokio/time\",\"tokio-stream\",\"experimental_async_runtime\"],\"spec_unstable_logs_enabled\":[\"logs\",\"opentelemetry/spec_unstable_logs_enabled\"],\"spec_unstable_metrics_views\":[\"metrics\"],\"testing\":[\"opentelemetry/testing\",\"trace\",\"metrics\",\"logs\",\"rt-tokio\",\"rt-tokio-current-thread\",\"tokio/macros\",\"tokio/rt-multi-thread\"],\"trace\":[\"opentelemetry/trace\",\"rand\",\"percent-encoding\"]}}", "option-ext_0.2.0": "{\"dependencies\":[],\"features\":{}}", "ordered-stream_0.2.0": "{\"dependencies\":[{\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"futures-executor\",\"req\":\"^0.3.25\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.25\"}],\"features\":{}}", - "os_info_3.12.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"plist\",\"req\":\"^1.5.1\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_LibraryLoader\",\"Win32_System_Registry\",\"Win32_System_SystemInformation\",\"Win32_System_SystemServices\",\"Win32_System_Threading\",\"Win32_UI_WindowsAndMessaging\"],\"name\":\"windows-sys\",\"req\":\"^0.52\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"serde\"]}}", - "os_pipe_1.2.2": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.62\",\"target\":\"cfg(not(windows))\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Pipes\",\"Win32_Security\"],\"name\":\"windows-sys\",\"req\":\"^0.59.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"io_safety\":[]}}", - "owo-colors_4.2.2": "{\"dependencies\":[{\"name\":\"supports-color\",\"optional\":true,\"req\":\"^3.0.0\"},{\"name\":\"supports-color-2\",\"optional\":true,\"package\":\"supports-color\",\"req\":\"^2.0\"}],\"features\":{\"alloc\":[],\"supports-colors\":[\"dep:supports-color-2\",\"supports-color\"]}}", + "os_info_3.14.0": "{\"dependencies\":[{\"name\":\"android_system_properties\",\"req\":\"^0.1\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"features\":[\"feature\"],\"name\":\"nix\",\"req\":\"^0.30\",\"target\":\"cfg(any(target_os = \\\"aix\\\", target_os = \\\"dragonfly\\\", target_os = \\\"freebsd\\\", target_os = \\\"illumos\\\", target_os = \\\"linux\\\", target_os = \\\"macos\\\", target_os = \\\"netbsd\\\", target_os = \\\"openbsd\\\", target_os = \\\"cygwin\\\"))\"},{\"name\":\"objc2\",\"req\":\"^0.6\",\"target\":\"cfg(target_os = \\\"ios\\\")\"},{\"features\":[\"NSString\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3\",\"target\":\"cfg(target_os = \\\"ios\\\")\"},{\"features\":[\"NSData\",\"NSError\",\"NSEnumerator\",\"NSString\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"name\":\"objc2-ui-kit\",\"req\":\"^0.3\",\"target\":\"cfg(target_os = \\\"ios\\\")\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1\"},{\"name\":\"schemars\",\"optional\":true,\"req\":\"^1.0.3\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_LibraryLoader\",\"Win32_System_Registry\",\"Win32_System_SystemInformation\",\"Win32_System_SystemServices\",\"Win32_System_Threading\",\"Win32_UI_WindowsAndMessaging\"],\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"serde\"]}}", + "os_pipe_1.2.3": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.62\",\"target\":\"cfg(not(windows))\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Pipes\",\"Win32_Security\"],\"name\":\"windows-sys\",\"req\":\">=0.28, <=0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"io_safety\":[]}}", + "owo-colors_4.2.3": "{\"dependencies\":[{\"name\":\"supports-color\",\"optional\":true,\"req\":\"^3.0.0\"},{\"name\":\"supports-color-2\",\"optional\":true,\"package\":\"supports-color\",\"req\":\"^2.0\"}],\"features\":{\"alloc\":[],\"supports-colors\":[\"dep:supports-color-2\",\"supports-color\"]}}", "parking_2.2.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"easy-parallel\",\"req\":\"^3.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"}],\"features\":{}}", - "parking_lot_0.12.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.3\"},{\"name\":\"lock_api\",\"req\":\"^0.4.13\"},{\"name\":\"parking_lot_core\",\"req\":\"^0.9.11\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.3\"}],\"features\":{\"arc_lock\":[\"lock_api/arc_lock\"],\"deadlock_detection\":[\"parking_lot_core/deadlock_detection\"],\"default\":[],\"hardware-lock-elision\":[],\"nightly\":[\"parking_lot_core/nightly\",\"lock_api/nightly\"],\"owning_ref\":[\"lock_api/owning_ref\"],\"send_guard\":[],\"serde\":[\"lock_api/serde\"]}}", - "parking_lot_core_0.9.11": "{\"dependencies\":[{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.60\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"libc\",\"req\":\"^0.2.95\",\"target\":\"cfg(unix)\"},{\"name\":\"petgraph\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"redox_syscall\",\"req\":\"^0.5\",\"target\":\"cfg(target_os = \\\"redox\\\")\"},{\"name\":\"smallvec\",\"req\":\"^1.6.1\"},{\"name\":\"thread-id\",\"optional\":true,\"req\":\"^4.0.0\"},{\"name\":\"windows-targets\",\"req\":\"^0.52.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"deadlock_detection\":[\"petgraph\",\"thread-id\",\"backtrace\"],\"nightly\":[]}}", + "parking_lot_0.12.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.3\"},{\"name\":\"lock_api\",\"req\":\"^0.4.14\"},{\"name\":\"parking_lot_core\",\"req\":\"^0.9.12\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.3\"}],\"features\":{\"arc_lock\":[\"lock_api/arc_lock\"],\"deadlock_detection\":[\"parking_lot_core/deadlock_detection\"],\"default\":[],\"hardware-lock-elision\":[],\"nightly\":[\"parking_lot_core/nightly\",\"lock_api/nightly\"],\"owning_ref\":[\"lock_api/owning_ref\"],\"send_guard\":[],\"serde\":[\"lock_api/serde\"]}}", + "parking_lot_core_0.9.12": "{\"dependencies\":[{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.60\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"libc\",\"req\":\"^0.2.95\",\"target\":\"cfg(unix)\"},{\"name\":\"petgraph\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"redox_syscall\",\"req\":\"^0.5\",\"target\":\"cfg(target_os = \\\"redox\\\")\"},{\"name\":\"smallvec\",\"req\":\"^1.6.1\"},{\"name\":\"windows-link\",\"req\":\"^0.2.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"deadlock_detection\":[\"petgraph\",\"backtrace\"],\"nightly\":[]}}", "paste_1.0.15": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"paste-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.49\"}],\"features\":{}}", - "pastey_0.2.0": "{\"dependencies\":[],\"features\":{}}", + "pastey_0.2.1": "{\"dependencies\":[],\"features\":{}}", "path-absolutize_3.1.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"path-dedot\",\"req\":\"^3.1.1\"},{\"kind\":\"dev\",\"name\":\"slash-formatter\",\"req\":\"^3\",\"target\":\"cfg(windows)\"}],\"features\":{\"lazy_static_cache\":[\"path-dedot/lazy_static_cache\"],\"once_cell_cache\":[\"path-dedot/once_cell_cache\"],\"unsafe_cache\":[\"path-dedot/unsafe_cache\"],\"use_unix_paths_on_wasm\":[\"path-dedot/use_unix_paths_on_wasm\"]}}", "path-dedot_3.1.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"lazy_static\",\"optional\":true,\"req\":\"^1.4\"},{\"name\":\"once_cell\",\"req\":\"^1.4\"}],\"features\":{\"lazy_static_cache\":[\"lazy_static\"],\"once_cell_cache\":[],\"unsafe_cache\":[],\"use_unix_paths_on_wasm\":[]}}", "pathdiff_0.2.3": "{\"dependencies\":[{\"name\":\"camino\",\"optional\":true,\"req\":\"^1.0.5\"},{\"kind\":\"dev\",\"name\":\"cfg-if\",\"req\":\"^1.0.0\"}],\"features\":{}}", + "pbkdf2_0.12.2": "{\"dependencies\":[{\"features\":[\"mac\"],\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"hmac\",\"req\":\"^0.12\"},{\"default_features\":false,\"features\":[\"rand_core\"],\"name\":\"password-hash\",\"optional\":true,\"req\":\"^0.5\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.7\"},{\"default_features\":false,\"name\":\"sha1\",\"optional\":true,\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"sha1\",\"req\":\"^0.10\"},{\"default_features\":false,\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"streebog\",\"req\":\"^0.10\"}],\"features\":{\"default\":[\"hmac\"],\"parallel\":[\"rayon\",\"std\"],\"simple\":[\"hmac\",\"password-hash\",\"sha2\"],\"std\":[\"password-hash/std\"]}}", "pem-rfc7468_0.7.0": "{\"dependencies\":[{\"name\":\"base64ct\",\"req\":\"^1.4\"}],\"features\":{\"alloc\":[\"base64ct/alloc\"],\"std\":[\"alloc\",\"base64ct/std\"]}}", - "percent-encoding_2.3.1": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", + "percent-encoding_2.3.2": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "petgraph_0.6.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"ahash\",\"req\":\"^0.7.2\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.3\"},{\"kind\":\"dev\",\"name\":\"defmac\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"name\":\"fixedbitset\",\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"fxhash\",\"req\":\"^0.2.1\"},{\"name\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.12.1\"},{\"kind\":\"dev\",\"name\":\"odds\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.5.5\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.5.3\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_derive\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"all\":[\"unstable\",\"quickcheck\",\"matrix_graph\",\"stable_graph\",\"graphmap\",\"rayon\"],\"default\":[\"graphmap\",\"stable_graph\",\"matrix_graph\"],\"generate\":[],\"graphmap\":[],\"matrix_graph\":[],\"rayon\":[\"dep:rayon\",\"indexmap/rayon\"],\"serde-1\":[\"serde\",\"serde_derive\"],\"stable_graph\":[],\"unstable\":[\"generate\"]}}", + "petgraph_0.8.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"ahash\",\"req\":\"^0.7.2\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.3\"},{\"kind\":\"dev\",\"name\":\"defmac\",\"req\":\"^0.2.1\"},{\"name\":\"dot-parser\",\"optional\":true,\"req\":\"^0.5.1\"},{\"name\":\"dot-parser-macros\",\"optional\":true,\"req\":\"^0.5.1\"},{\"default_features\":false,\"name\":\"fixedbitset\",\"req\":\"^0.5.7\"},{\"kind\":\"dev\",\"name\":\"fxhash\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"features\":[\"default-hasher\",\"inline-more\"],\"name\":\"hashbrown\",\"req\":\"^0.15.0\"},{\"default_features\":false,\"name\":\"indexmap\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.12.1\"},{\"kind\":\"dev\",\"name\":\"odds\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.5.5\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.5.3\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_derive\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"all\":[\"unstable\",\"quickcheck\",\"matrix_graph\",\"stable_graph\",\"graphmap\",\"rayon\",\"dot_parser\"],\"default\":[\"std\",\"graphmap\",\"stable_graph\",\"matrix_graph\"],\"dot_parser\":[\"std\",\"dep:dot-parser\",\"dep:dot-parser-macros\"],\"generate\":[],\"graphmap\":[],\"matrix_graph\":[],\"quickcheck\":[\"std\",\"dep:quickcheck\",\"graphmap\",\"stable_graph\"],\"rayon\":[\"std\",\"dep:rayon\",\"indexmap/rayon\",\"hashbrown/rayon\"],\"serde-1\":[\"serde\",\"serde_derive\"],\"stable_graph\":[\"serde?/alloc\"],\"std\":[\"indexmap/std\"],\"unstable\":[\"generate\"]}}", "phf_shared_0.11.3": "{\"dependencies\":[{\"name\":\"siphasher\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"uncased\",\"optional\":true,\"req\":\"^0.9.9\"},{\"name\":\"unicase\",\"optional\":true,\"req\":\"^2.4.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "pin-project-internal_1.1.10": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1.0.25\"},{\"default_features\":false,\"features\":[\"parsing\",\"printing\",\"clone-impls\",\"proc-macro\",\"full\",\"visit-mut\"],\"name\":\"syn\",\"req\":\"^2.0.1\"}],\"features\":{}}", "pin-project-lite_0.2.16": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1\"}],\"features\":{}}", @@ -990,11 +1032,11 @@ "pkcs1_0.7.5": "{\"dependencies\":[{\"features\":[\"db\"],\"kind\":\"dev\",\"name\":\"const-oid\",\"req\":\"^0.9\"},{\"features\":[\"oid\"],\"name\":\"der\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"pkcs8\",\"optional\":true,\"req\":\"^0.10\"},{\"name\":\"spki\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"alloc\":[\"der/alloc\",\"zeroize\",\"pkcs8?/alloc\"],\"pem\":[\"alloc\",\"der/pem\",\"pkcs8?/pem\"],\"std\":[\"der/std\",\"alloc\"],\"zeroize\":[\"der/zeroize\"]}}", "pkcs8_0.10.2": "{\"dependencies\":[{\"features\":[\"oid\"],\"name\":\"der\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3\"},{\"name\":\"pkcs5\",\"optional\":true,\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"rand_core\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"spki\",\"req\":\"^0.7.1\"},{\"default_features\":false,\"name\":\"subtle\",\"optional\":true,\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"3des\":[\"encryption\",\"pkcs5/3des\"],\"alloc\":[\"der/alloc\",\"der/zeroize\",\"spki/alloc\"],\"des-insecure\":[\"encryption\",\"pkcs5/des-insecure\"],\"encryption\":[\"alloc\",\"pkcs5/alloc\",\"pkcs5/pbes2\",\"rand_core\"],\"getrandom\":[\"rand_core/getrandom\"],\"pem\":[\"alloc\",\"der/pem\",\"spki/pem\"],\"sha1-insecure\":[\"encryption\",\"pkcs5/sha1-insecure\"],\"std\":[\"alloc\",\"der/std\",\"spki/std\"]}}", "pkg-config_0.3.32": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"}],\"features\":{}}", - "plist_1.7.4": "{\"dependencies\":[{\"name\":\"base64\",\"req\":\"^0.22.0\"},{\"name\":\"indexmap\",\"req\":\"^2.1.0\"},{\"name\":\"quick_xml\",\"package\":\"quick-xml\",\"req\":\"^0.38.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"serde_yaml\",\"req\":\"^0.8.21\"},{\"features\":[\"parsing\",\"formatting\"],\"name\":\"time\",\"req\":\"^0.3.30\"}],\"features\":{\"default\":[\"serde\"],\"enable_unstable_features_that_may_break_with_minor_version_bumps\":[]}}", "png_0.18.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"approx\",\"req\":\"^0.5.1\"},{\"name\":\"bitflags\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"byteorder\",\"req\":\"^1.5.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.0\"},{\"name\":\"crc32fast\",\"req\":\"^1.2.0\"},{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"fdeflate\",\"req\":\"^0.3.3\"},{\"name\":\"flate2\",\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3\"},{\"features\":[\"simd\"],\"name\":\"miniz_oxide\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9.2\"}],\"features\":{\"benchmarks\":[],\"unstable\":[\"crc32fast/nightly\"],\"zlib-rs\":[\"flate2/zlib-rs\"]}}", "polling_3.11.0": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"concurrent-queue\",\"req\":\"^2.2.0\",\"target\":\"cfg(windows)\"},{\"kind\":\"dev\",\"name\":\"easy-parallel\",\"req\":\"^3.1.0\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2.0.0\"},{\"name\":\"hermit-abi\",\"req\":\"^0.5.0\",\"target\":\"cfg(target_os = \\\"hermit\\\")\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.9\",\"target\":\"cfg(windows)\"},{\"default_features\":false,\"features\":[\"event\",\"fs\",\"pipe\",\"process\",\"std\",\"time\"],\"name\":\"rustix\",\"req\":\"^1.0.5\",\"target\":\"cfg(any(unix, target_os = \\\"fuchsia\\\", target_os = \\\"vxworks\\\"))\"},{\"kind\":\"dev\",\"name\":\"signal-hook\",\"req\":\"^0.3.17\",\"target\":\"cfg(all(unix, not(target_os=\\\"vita\\\")))\"},{\"kind\":\"dev\",\"name\":\"socket2\",\"req\":\"^0.6.0\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.37\"},{\"features\":[\"Wdk_Foundation\",\"Wdk_Storage_FileSystem\",\"Win32_Foundation\",\"Win32_Networking_WinSock\",\"Win32_Security\",\"Win32_Storage_FileSystem\",\"Win32_System_IO\",\"Win32_System_LibraryLoader\",\"Win32_System_Threading\",\"Win32_System_WindowsProgramming\"],\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{}}", - "portable-atomic-util_0.2.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"build-context\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"require-cas\"],\"name\":\"portable-atomic\",\"req\":\"^1.5.1\"}],\"features\":{\"alloc\":[],\"default\":[],\"std\":[\"alloc\"]}}", - "portable-atomic_1.11.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"build-context\",\"req\":\"^0.1\"},{\"name\":\"critical-section\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"crossbeam-utils\",\"req\":\"=0.8.16\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"=0.2.163\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.60\"},{\"kind\":\"dev\",\"name\":\"sptr\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Threading\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.59\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"fallback\"],\"disable-fiq\":[],\"fallback\":[],\"float\":[],\"force-amo\":[],\"require-cas\":[],\"s-mode\":[],\"std\":[],\"unsafe-assume-single-core\":[]}}", + "poly1305_0.8.0": "{\"dependencies\":[{\"name\":\"cpufeatures\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_arch = \\\"x86_64\\\", target_arch = \\\"x86\\\"))\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3\"},{\"name\":\"opaque-debug\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"universal-hash\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"std\":[\"universal-hash/std\"]}}", + "portable-atomic-util_0.2.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"build-context\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"require-cas\"],\"name\":\"portable-atomic\",\"req\":\"^1.5.1\"}],\"features\":{\"alloc\":[],\"default\":[],\"std\":[\"alloc\"]}}", + "portable-atomic_1.13.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"build-context\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"crabgrind\",\"req\":\"^0.1\",\"target\":\"cfg(valgrind)\"},{\"name\":\"critical-section\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"crossbeam-utils\",\"req\":\"=0.8.16\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"=0.2.163\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.60\"},{\"kind\":\"dev\",\"name\":\"sptr\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Threading\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"fallback\"],\"disable-fiq\":[],\"fallback\":[],\"float\":[],\"force-amo\":[],\"require-cas\":[],\"s-mode\":[],\"std\":[],\"unsafe-assume-privileged\":[],\"unsafe-assume-single-core\":[]}}", "portable-pty_0.9.0": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"bitflags\",\"req\":\"^1.3\",\"target\":\"cfg(windows)\"},{\"name\":\"downcast-rs\",\"req\":\"^1.0\"},{\"name\":\"filedescriptor\",\"req\":\"^0.8.3\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"lazy_static\",\"req\":\"^1.4\",\"target\":\"cfg(windows)\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"features\":[\"term\",\"fs\"],\"name\":\"nix\",\"req\":\"^0.28\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_derive\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serial2\",\"req\":\"^0.2\"},{\"name\":\"shared_library\",\"req\":\"^0.1\",\"target\":\"cfg(windows)\"},{\"name\":\"shell-words\",\"req\":\"^1.1\"},{\"kind\":\"dev\",\"name\":\"smol\",\"req\":\"^2.0\"},{\"features\":[\"winuser\",\"consoleapi\",\"handleapi\",\"fileapi\",\"namedpipeapi\",\"synchapi\"],\"name\":\"winapi\",\"req\":\"^0.3\",\"target\":\"cfg(windows)\"},{\"name\":\"winreg\",\"req\":\"^0.10\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[],\"serde_support\":[\"serde\",\"serde_derive\"]}}", "potential_utf_0.1.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"default_features\":false,\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"name\":\"writeable\",\"optional\":true,\"req\":\"^0.6.0\"},{\"default_features\":false,\"name\":\"zerovec\",\"optional\":true,\"req\":\"^0.11.3\"}],\"features\":{\"alloc\":[\"serde_core?/alloc\",\"writeable/alloc\",\"zerovec?/alloc\"],\"databake\":[\"dep:databake\"],\"default\":[\"alloc\"],\"serde\":[\"dep:serde_core\"],\"writeable\":[\"dep:writeable\"],\"zerovec\":[\"dep:zerovec\"]}}", "powerfmt_0.2.0": "{\"dependencies\":[{\"name\":\"powerfmt-macros\",\"optional\":true,\"req\":\"=0.1.0\"}],\"features\":{\"alloc\":[],\"default\":[\"std\",\"macros\"],\"macros\":[\"dep:powerfmt-macros\"],\"std\":[\"alloc\"]}}", @@ -1005,29 +1047,30 @@ "predicates_3.1.3": "{\"dependencies\":[{\"name\":\"anstyle\",\"req\":\"^1.0.0\"},{\"name\":\"difflib\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"float-cmp\",\"optional\":true,\"req\":\"^0.10\"},{\"name\":\"normalize-line-endings\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"predicates-core\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"predicates-tree\",\"req\":\"^1.0\"},{\"name\":\"regex\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"color\":[],\"default\":[\"diff\",\"regex\",\"float-cmp\",\"normalize-line-endings\",\"color\"],\"diff\":[\"dep:difflib\"],\"unstable\":[]}}", "pretty_assertions_1.4.1": "{\"dependencies\":[{\"name\":\"diff\",\"req\":\"^0.1.12\"},{\"name\":\"yansi\",\"req\":\"^1.0.1\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[],\"unstable\":[]}}", "proc-macro-crate_3.4.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.94\"},{\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0.39\"},{\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0.99\"},{\"default_features\":false,\"features\":[\"parse\"],\"name\":\"toml_edit\",\"req\":\"^0.23.2\"}],\"features\":{}}", - "proc-macro2_1.0.95": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tar\",\"req\":\"^0.4\"},{\"name\":\"unicode-ident\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"proc-macro\"],\"nightly\":[],\"proc-macro\":[],\"span-locations\":[]}}", - "process-wrap_9.0.0": "{\"dependencies\":[{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.30\"},{\"name\":\"indexmap\",\"req\":\"^2.9.0\"},{\"default_features\":false,\"features\":[\"fs\",\"poll\",\"signal\"],\"name\":\"nix\",\"optional\":true,\"req\":\"^0.30.1\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"remoteprocess\",\"req\":\"^0.5.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.20.0\"},{\"features\":[\"io-util\",\"macros\",\"process\",\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.38.2\"},{\"features\":[\"io-util\",\"macros\",\"process\",\"rt\",\"rt-multi-thread\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.38.2\"},{\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.40\"},{\"name\":\"windows\",\"optional\":true,\"req\":\"^0.61.1\",\"target\":\"cfg(windows)\"}],\"features\":{\"creation-flags\":[\"dep:windows\",\"windows/Win32_System_Threading\"],\"default\":[\"creation-flags\",\"job-object\",\"kill-on-drop\",\"process-group\",\"process-session\",\"tracing\"],\"job-object\":[\"dep:windows\",\"windows/Win32_Security\",\"windows/Win32_System_Diagnostics_ToolHelp\",\"windows/Win32_System_IO\",\"windows/Win32_System_JobObjects\",\"windows/Win32_System_Threading\"],\"kill-on-drop\":[],\"process-group\":[],\"process-session\":[\"process-group\"],\"reset-sigmask\":[],\"std\":[\"dep:nix\"],\"tokio1\":[\"dep:nix\",\"dep:futures\",\"dep:tokio\"],\"tracing\":[\"dep:tracing\"]}}", + "proc-macro-error-attr2_2.0.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"}],\"features\":{}}", + "proc-macro-error2_2.0.1": "{\"dependencies\":[{\"name\":\"proc-macro-error-attr2\",\"req\":\"=2.0.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"syn\",\"optional\":true,\"req\":\"^2\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.99\"}],\"features\":{\"default\":[\"syn-error\"],\"nightly\":[],\"syn-error\":[\"dep:syn\"]}}", + "proc-macro2_1.0.106": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tar\",\"req\":\"^0.4\"},{\"name\":\"unicode-ident\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"proc-macro\"],\"nightly\":[],\"proc-macro\":[],\"span-locations\":[]}}", + "process-wrap_9.0.1": "{\"dependencies\":[{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.30\"},{\"name\":\"indexmap\",\"req\":\"^2.9.0\"},{\"default_features\":false,\"features\":[\"fs\",\"poll\",\"signal\"],\"name\":\"nix\",\"optional\":true,\"req\":\"^0.30.1\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"remoteprocess\",\"req\":\"^0.5.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.20.0\"},{\"features\":[\"io-util\",\"macros\",\"process\",\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.38.2\"},{\"features\":[\"io-util\",\"macros\",\"process\",\"rt\",\"rt-multi-thread\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.38.2\"},{\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.40\"},{\"name\":\"windows\",\"optional\":true,\"req\":\"^0.62.2\",\"target\":\"cfg(windows)\"}],\"features\":{\"creation-flags\":[\"dep:windows\",\"windows/Win32_System_Threading\"],\"default\":[\"creation-flags\",\"job-object\",\"kill-on-drop\",\"process-group\",\"process-session\",\"tracing\"],\"job-object\":[\"dep:windows\",\"windows/Win32_Security\",\"windows/Win32_System_Diagnostics_ToolHelp\",\"windows/Win32_System_IO\",\"windows/Win32_System_JobObjects\",\"windows/Win32_System_Threading\"],\"kill-on-drop\":[],\"process-group\":[],\"process-session\":[\"process-group\"],\"reset-sigmask\":[],\"std\":[\"dep:nix\"],\"tokio1\":[\"dep:nix\",\"dep:futures\",\"dep:tokio\"],\"tracing\":[\"dep:tracing\"]}}", "proptest_1.9.0": "{\"dependencies\":[{\"name\":\"bit-set\",\"optional\":true,\"req\":\"^0.8.0\"},{\"name\":\"bit-vec\",\"optional\":true,\"req\":\"^0.8.0\"},{\"name\":\"bitflags\",\"req\":\"^2.9\"},{\"default_features\":false,\"name\":\"num-traits\",\"req\":\"^0.2.15\"},{\"name\":\"proptest-macro\",\"optional\":true,\"req\":\"^0.4.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"rand\",\"req\":\"^0.9\"},{\"default_features\":false,\"name\":\"rand_chacha\",\"req\":\"^0.9\"},{\"name\":\"rand_xorshift\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.0\"},{\"name\":\"regex-syntax\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"rusty-fork\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"tempfile\",\"optional\":true,\"req\":\"^3.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"=1.0.112\"},{\"name\":\"unarray\",\"req\":\"^0.1.4\"},{\"name\":\"x86\",\"optional\":true,\"req\":\"^0.52.0\"}],\"features\":{\"alloc\":[],\"atomic64bit\":[],\"attr-macro\":[\"proptest-macro\"],\"bit-set\":[\"dep:bit-set\",\"dep:bit-vec\"],\"default\":[\"std\",\"fork\",\"timeout\",\"bit-set\"],\"default-code-coverage\":[\"std\",\"fork\",\"timeout\",\"bit-set\"],\"fork\":[\"std\",\"rusty-fork\",\"tempfile\"],\"handle-panics\":[\"std\"],\"hardware-rng\":[\"x86\"],\"no_std\":[\"num-traits/libm\"],\"std\":[\"rand/std\",\"rand/os_rng\",\"regex-syntax\",\"num-traits/std\"],\"timeout\":[\"fork\",\"rusty-fork/timeout\"],\"unstable\":[]}}", - "prost-derive_0.14.1": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0.1\"},{\"name\":\"itertools\",\"req\":\">=0.10.1, <=0.14\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", - "prost_0.14.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"name\":\"prost-derive\",\"optional\":true,\"req\":\"^0.14.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"}],\"features\":{\"default\":[\"derive\",\"std\"],\"derive\":[\"dep:prost-derive\"],\"no-recursion-limit\":[],\"std\":[]}}", + "prost-derive_0.14.3": "{\"dependencies\":[{\"name\":\"anyhow\",\"req\":\"^1.0.1\"},{\"name\":\"itertools\",\"req\":\">=0.10.1, <=0.14\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", + "prost_0.14.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"name\":\"prost-derive\",\"optional\":true,\"req\":\"^0.14.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"}],\"features\":{\"default\":[\"derive\",\"std\"],\"derive\":[\"dep:prost-derive\"],\"no-recursion-limit\":[],\"std\":[]}}", "psl-types_2.0.11": "{\"dependencies\":[],\"features\":{}}", - "psl_2.1.178": "{\"dependencies\":[{\"name\":\"psl-types\",\"req\":\"^2.0.11\"},{\"kind\":\"dev\",\"name\":\"rspec\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[\"helpers\"],\"helpers\":[]}}", + "psl_2.1.184": "{\"dependencies\":[{\"name\":\"psl-types\",\"req\":\"^2.0.11\"},{\"kind\":\"dev\",\"name\":\"rspec\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[\"helpers\"],\"helpers\":[]}}", "pulldown-cmark-escape_0.10.1": "{\"dependencies\":[],\"features\":{\"simd\":[]}}", "pulldown-cmark_0.10.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"name\":\"bitflags\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"getopts\",\"optional\":true,\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.4\"},{\"name\":\"memchr\",\"req\":\"^2.5\"},{\"name\":\"pulldown-cmark-escape\",\"optional\":true,\"req\":\"^0.10.0\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.6\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.61\"},{\"name\":\"unicase\",\"req\":\"^2.6\"}],\"features\":{\"default\":[\"getopts\",\"html\"],\"gen-tests\":[],\"html\":[\"pulldown-cmark-escape\"],\"simd\":[\"pulldown-cmark-escape?/simd\"]}}", - "pxfm_0.1.23": "{\"dependencies\":[{\"name\":\"num-traits\",\"req\":\"^0.2\"}],\"features\":{}}", + "pxfm_0.1.27": "{\"dependencies\":[{\"name\":\"num-traits\",\"req\":\"^0.2.3\"}],\"features\":{}}", "quick-error_2.0.1": "{\"dependencies\":[],\"features\":{}}", - "quick-xml_0.37.5": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"memchr\",\"req\":\"^2.1\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1\"},{\"name\":\"serde\",\"optional\":true,\"req\":\">=1.0.139\"},{\"kind\":\"dev\",\"name\":\"serde-value\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.206\"},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.10\"},{\"default_features\":false,\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.21\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"}],\"features\":{\"async-tokio\":[\"tokio\"],\"default\":[],\"encoding\":[\"encoding_rs\"],\"escape-html\":[],\"overlapped-lists\":[],\"serde-types\":[\"serde/derive\"],\"serialize\":[\"serde\"]}}", - "quick-xml_0.38.0": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\">=0.4, <0.7\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"memchr\",\"req\":\"^2.1\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1\"},{\"name\":\"serde\",\"optional\":true,\"req\":\">=1.0.139\"},{\"kind\":\"dev\",\"name\":\"serde-value\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.206\"},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.10\"},{\"default_features\":false,\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.21\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"}],\"features\":{\"async-tokio\":[\"tokio\"],\"default\":[],\"encoding\":[\"encoding_rs\"],\"escape-html\":[],\"overlapped-lists\":[],\"serde-types\":[\"serde/derive\"],\"serialize\":[\"serde\"]}}", + "quick-xml_0.38.4": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\">=0.4, <0.8\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"memchr\",\"req\":\"^2.1\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1\"},{\"name\":\"serde\",\"optional\":true,\"req\":\">=1.0.139\"},{\"kind\":\"dev\",\"name\":\"serde-value\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.206\"},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.10\"},{\"default_features\":false,\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.21\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"}],\"features\":{\"async-tokio\":[\"tokio\"],\"default\":[],\"encoding\":[\"encoding_rs\"],\"escape-html\":[],\"overlapped-lists\":[],\"serde-types\":[\"serde/derive\"],\"serialize\":[\"serde\"]}}", "quinn-proto_0.11.13": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.1\"},{\"default_features\":false,\"name\":\"aws-lc-rs\",\"optional\":true,\"req\":\"^1.9\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"fastbloom\",\"optional\":true,\"req\":\"^0.14\"},{\"default_features\":false,\"features\":[\"wasm_js\"],\"name\":\"getrandom\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"lru-slab\",\"req\":\"^0.1.2\"},{\"name\":\"qlog\",\"optional\":true,\"req\":\"^0.15.2\"},{\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"features\":[\"wasm32_unknown_unknown_js\"],\"name\":\"ring\",\"req\":\"^0.17\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"ring\",\"optional\":true,\"req\":\"^0.17\"},{\"name\":\"rustc-hash\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.5\"},{\"features\":[\"web\"],\"name\":\"rustls-pki-types\",\"req\":\"^1.7\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"rustls-platform-verifier\",\"optional\":true,\"req\":\"^0.6\"},{\"name\":\"slab\",\"req\":\"^0.4.6\"},{\"name\":\"thiserror\",\"req\":\"^2.0.3\"},{\"features\":[\"alloc\",\"alloc\"],\"name\":\"tinyvec\",\"req\":\"^1.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.10\"},{\"default_features\":false,\"features\":[\"env-filter\",\"fmt\",\"ansi\",\"time\",\"local-time\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.45\"},{\"name\":\"web-time\",\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"}],\"features\":{\"aws-lc-rs\":[\"dep:aws-lc-rs\",\"aws-lc-rs?/aws-lc-sys\",\"aws-lc-rs?/prebuilt-nasm\"],\"aws-lc-rs-fips\":[\"aws-lc-rs\",\"aws-lc-rs?/fips\"],\"bloom\":[\"dep:fastbloom\"],\"default\":[\"rustls-ring\",\"log\",\"bloom\"],\"log\":[\"tracing/log\"],\"platform-verifier\":[\"dep:rustls-platform-verifier\"],\"qlog\":[\"dep:qlog\"],\"ring\":[\"dep:ring\"],\"rustls\":[\"rustls-ring\"],\"rustls-aws-lc-rs\":[\"dep:rustls\",\"rustls?/aws-lc-rs\",\"aws-lc-rs\"],\"rustls-aws-lc-rs-fips\":[\"rustls-aws-lc-rs\",\"aws-lc-rs-fips\"],\"rustls-log\":[\"rustls?/logging\"],\"rustls-ring\":[\"dep:rustls\",\"rustls?/ring\",\"ring\"]}}", "quinn-udp_0.5.14": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cfg_aliases\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"async_tokio\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"name\":\"libc\",\"req\":\"^0.2.158\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"req\":\"^1.19\",\"target\":\"cfg(windows)\"},{\"name\":\"socket2\",\"req\":\">=0.5, <0.7\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"features\":[\"sync\",\"rt\",\"rt-multi-thread\",\"net\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.28.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.10\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_IO\",\"Win32_Networking_WinSock\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <=0.60\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"tracing\",\"log\"],\"direct-log\":[\"dep:log\"],\"fast-apple-datapath\":[],\"log\":[\"tracing/log\"]}}", "quinn_0.11.9": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.22\"},{\"name\":\"async-io\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"async-std\",\"optional\":true,\"req\":\"^1.11\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"build\",\"name\":\"cfg_aliases\",\"req\":\"^0.2\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4\"},{\"kind\":\"dev\",\"name\":\"crc\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"directories-next\",\"req\":\"^2\"},{\"name\":\"futures-io\",\"optional\":true,\"req\":\"^0.3.19\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"proto\",\"package\":\"quinn-proto\",\"req\":\"^0.11.12\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"name\":\"rustc-hash\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.5\"},{\"kind\":\"dev\",\"name\":\"rustls-pemfile\",\"req\":\"^2\"},{\"name\":\"smol\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"socket2\",\"req\":\">=0.5, <0.7\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"thiserror\",\"req\":\"^2.0.3\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1.28.1\"},{\"features\":[\"sync\",\"rt\",\"rt-multi-thread\",\"time\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.28.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.10\"},{\"default_features\":false,\"features\":[\"std-future\"],\"kind\":\"dev\",\"name\":\"tracing-futures\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"env-filter\",\"fmt\",\"ansi\",\"time\",\"local-time\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"default_features\":false,\"features\":[\"tracing\"],\"name\":\"udp\",\"package\":\"quinn-udp\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"url\",\"req\":\"^2\"},{\"name\":\"web-time\",\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"}],\"features\":{\"aws-lc-rs\":[\"proto/aws-lc-rs\"],\"aws-lc-rs-fips\":[\"proto/aws-lc-rs-fips\"],\"bloom\":[\"proto/bloom\"],\"default\":[\"log\",\"platform-verifier\",\"runtime-tokio\",\"rustls-ring\",\"bloom\"],\"lock_tracking\":[],\"log\":[\"tracing/log\",\"proto/log\",\"udp/log\"],\"platform-verifier\":[\"proto/platform-verifier\"],\"qlog\":[\"proto/qlog\"],\"ring\":[\"proto/ring\"],\"runtime-async-std\":[\"async-io\",\"async-std\"],\"runtime-smol\":[\"async-io\",\"smol\"],\"runtime-tokio\":[\"tokio/time\",\"tokio/rt\",\"tokio/net\"],\"rustls\":[\"rustls-ring\"],\"rustls-aws-lc-rs\":[\"dep:rustls\",\"aws-lc-rs\",\"proto/rustls-aws-lc-rs\",\"proto/aws-lc-rs\"],\"rustls-aws-lc-rs-fips\":[\"dep:rustls\",\"aws-lc-rs-fips\",\"proto/rustls-aws-lc-rs-fips\",\"proto/aws-lc-rs-fips\"],\"rustls-log\":[\"rustls?/logging\"],\"rustls-ring\":[\"dep:rustls\",\"ring\",\"proto/rustls-ring\",\"proto/ring\"]}}", - "quote_1.0.40": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.80\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.66\"}],\"features\":{\"default\":[\"proc-macro\"],\"proc-macro\":[\"proc-macro2/proc-macro\"]}}", + "quote_1.0.44": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.80\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{\"default\":[\"proc-macro\"],\"proc-macro\":[\"proc-macro2/proc-macro\"]}}", "r-efi_5.3.0": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"efiapi\":[],\"examples\":[\"native\"],\"native\":[],\"rustc-dep-of-std\":[\"core\"]}}", "radix_trie_0.2.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"name\":\"endian-type\",\"req\":\"^0.1.2\"},{\"name\":\"nibble_vec\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.3\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{}}", "radix_trie_0.3.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"name\":\"endian-type\",\"req\":\"^0.2.0\"},{\"name\":\"nibble_vec\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"}],\"features\":{}}", - "rama-boring-sys_0.5.9": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"runtime\"],\"kind\":\"build\",\"name\":\"bindgen\",\"req\":\"^0.72.0\"},{\"kind\":\"build\",\"name\":\"cmake\",\"req\":\"^0.1.54\"},{\"kind\":\"build\",\"name\":\"fs_extra\",\"req\":\"^1.3.0\"},{\"kind\":\"build\",\"name\":\"fslock\",\"req\":\"^0.2\"}],\"features\":{}}", - "rama-boring-tokio_0.5.9": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"rama-boring\",\"req\":\"^0.5.9\"},{\"name\":\"rama-boring-sys\",\"req\":\"^0.5.9\"},{\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"}],\"features\":{}}", - "rama-boring_0.5.9": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.9\"},{\"kind\":\"dev\",\"name\":\"brotli\",\"req\":\"^8.0\"},{\"name\":\"foreign-types\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"openssl-macros\",\"req\":\"^0.1.1\"},{\"name\":\"rama-boring-sys\",\"req\":\"^0.5.9\"},{\"kind\":\"dev\",\"name\":\"rusty-hook\",\"req\":\"^0.11\"}],\"features\":{}}", + "rama-boring-sys_0.5.10": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"runtime\"],\"kind\":\"build\",\"name\":\"bindgen\",\"req\":\"^0.72.0\"},{\"kind\":\"build\",\"name\":\"cmake\",\"req\":\"^0.1.54\"},{\"kind\":\"build\",\"name\":\"fs_extra\",\"req\":\"^1.3.0\"},{\"kind\":\"build\",\"name\":\"fslock\",\"req\":\"^0.2\"}],\"features\":{}}", + "rama-boring-tokio_0.5.10": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"rama-boring\",\"req\":\"^0.5.10\"},{\"name\":\"rama-boring-sys\",\"req\":\"^0.5.10\"},{\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"}],\"features\":{}}", + "rama-boring_0.5.10": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.9\"},{\"kind\":\"dev\",\"name\":\"brotli\",\"req\":\"^8.0\"},{\"name\":\"foreign-types\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"openssl-macros\",\"req\":\"^0.1.1\"},{\"name\":\"rama-boring-sys\",\"req\":\"^0.5.10\"},{\"kind\":\"dev\",\"name\":\"rusty-hook\",\"req\":\"^0.11\"}],\"features\":{}}", "rama-core_0.3.0-alpha.4": "{\"dependencies\":[{\"name\":\"ahash\",\"req\":\"^0.8\"},{\"name\":\"asynk-strim\",\"req\":\"^0.1\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"trace\"],\"name\":\"opentelemetry\",\"optional\":true,\"req\":\"^0.31\"},{\"features\":[\"semconv_experimental\"],\"name\":\"opentelemetry-semantic-conventions\",\"optional\":true,\"req\":\"^0.31\"},{\"default_features\":false,\"features\":[\"trace\",\"rt-tokio\"],\"name\":\"opentelemetry_sdk\",\"optional\":true,\"req\":\"^0.31\"},{\"name\":\"parking_lot\",\"req\":\"^0.12\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"name\":\"rama-error\",\"req\":\"^0.3.0-alpha.4\"},{\"name\":\"rama-macros\",\"req\":\"^0.3.0-alpha.4\"},{\"name\":\"rama-utils\",\"req\":\"^0.3.0-alpha.4\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"macros\",\"fs\",\"io-std\"],\"name\":\"tokio\",\"req\":\"^1.48\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.48\"},{\"name\":\"tokio-graceful\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"},{\"features\":[\"codec\",\"io\",\"io-util\"],\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"name\":\"tracing-opentelemetry\",\"optional\":true,\"req\":\"^0.32\"}],\"features\":{\"default\":[],\"opentelemetry\":[\"dep:opentelemetry\",\"dep:opentelemetry-semantic-conventions\",\"dep:opentelemetry_sdk\",\"dep:tracing-opentelemetry\"]}}", "rama-dns_0.3.0-alpha.4": "{\"dependencies\":[{\"name\":\"ahash\",\"req\":\"^0.8\"},{\"default_features\":false,\"features\":[\"tokio\",\"system-config\"],\"name\":\"hickory-resolver\",\"req\":\"^0.25\"},{\"name\":\"rama-core\",\"req\":\"^0.3.0-alpha.4\"},{\"name\":\"rama-net\",\"req\":\"^0.3.0-alpha.4\"},{\"name\":\"rama-utils\",\"req\":\"^0.3.0-alpha.4\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_html_form\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"macros\",\"net\"],\"name\":\"tokio\",\"req\":\"^1.48\"}],\"features\":{\"default\":[]}}", "rama-error_0.3.0-alpha.4": "{\"dependencies\":[],\"features\":{}}", @@ -1049,39 +1092,45 @@ "rand_chacha_0.3.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"simd\"],\"name\":\"ppv-lite86\",\"req\":\"^0.2.8\"},{\"name\":\"rand_core\",\"req\":\"^0.6.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"serde1\":[\"serde\"],\"simd\":[],\"std\":[\"ppv-lite86/std\"]}}", "rand_chacha_0.9.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"simd\"],\"name\":\"ppv-lite86\",\"req\":\"^0.2.14\"},{\"name\":\"rand_core\",\"req\":\"^0.9.0\"},{\"features\":[\"os_rng\"],\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.9.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"os_rng\":[\"rand_core/os_rng\"],\"serde\":[\"dep:serde\"],\"std\":[\"ppv-lite86/std\",\"rand_core/std\"]}}", "rand_core_0.6.4": "{\"dependencies\":[{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.2\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[],\"serde1\":[\"serde\"],\"std\":[\"alloc\",\"getrandom\",\"getrandom/std\"]}}", - "rand_core_0.9.3": "{\"dependencies\":[{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"os_rng\":[\"dep:getrandom\"],\"serde\":[\"dep:serde\"],\"std\":[\"getrandom?/std\"]}}", + "rand_core_0.9.5": "{\"dependencies\":[{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"os_rng\":[\"dep:getrandom\"],\"serde\":[\"dep:serde\"],\"std\":[\"getrandom?/std\"]}}", "rand_xorshift_0.4.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1\"},{\"name\":\"rand_core\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.118\"}],\"features\":{\"serde\":[\"dep:serde\"]}}", "ratatui-macros_0.6.0": "{\"dependencies\":[{\"features\":[\"user-hooks\"],\"kind\":\"dev\",\"name\":\"cargo-husky\",\"req\":\"^1.5.0\"},{\"name\":\"ratatui\",\"req\":\"^0.29.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.101\"}],\"features\":{}}", "rayon-core_1.13.0": "{\"dependencies\":[{\"name\":\"crossbeam-deque\",\"req\":\"^0.8.1\"},{\"name\":\"crossbeam-utils\",\"req\":\"^0.8.0\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"scoped-tls\",\"req\":\"^1.0\"},{\"name\":\"wasm_sync\",\"optional\":true,\"req\":\"^0.1.0\"}],\"features\":{\"web_spin_lock\":[\"dep:wasm_sync\"]}}", "rayon_1.11.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"either\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.4\"},{\"name\":\"rayon-core\",\"req\":\"^1.13.0\"},{\"name\":\"wasm_sync\",\"optional\":true,\"req\":\"^0.1.0\"}],\"features\":{\"web_spin_lock\":[\"dep:wasm_sync\",\"rayon-core/web_spin_lock\"]}}", - "redox_syscall_0.5.15": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.4\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"}],\"features\":{\"default\":[\"userspace\"],\"rustc-dep-of-std\":[\"core\",\"bitflags/rustc-dep-of-std\"],\"std\":[],\"userspace\":[]}}", + "redox_syscall_0.5.18": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.4\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"}],\"features\":{\"default\":[\"userspace\"],\"rustc-dep-of-std\":[\"core\",\"bitflags/rustc-dep-of-std\"],\"std\":[],\"userspace\":[]}}", + "redox_syscall_0.7.0": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.4\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"}],\"features\":{\"default\":[\"userspace\"],\"rustc-dep-of-std\":[\"core\",\"bitflags/rustc-dep-of-std\"],\"std\":[],\"userspace\":[]}}", "redox_users_0.4.6": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"std\",\"call\"],\"name\":\"libredox\",\"req\":\"^0.1.3\"},{\"name\":\"rust-argon2\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"features\":[\"zeroize_derive\"],\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.4\"}],\"features\":{\"auth\":[\"rust-argon2\",\"zeroize\"],\"default\":[\"auth\"]}}", - "redox_users_0.5.0": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"std\",\"call\"],\"name\":\"libredox\",\"req\":\"^0.1.3\"},{\"name\":\"rust-argon2\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"thiserror\",\"req\":\"^2.0\"},{\"features\":[\"zeroize_derive\"],\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.4\"}],\"features\":{\"auth\":[\"rust-argon2\",\"zeroize\"],\"default\":[\"auth\"]}}", - "ref-cast-impl_1.0.24": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1\"},{\"name\":\"syn\",\"req\":\"^2.0.46\"}],\"features\":{}}", - "ref-cast_1.0.24": "{\"dependencies\":[{\"name\":\"ref-cast-impl\",\"req\":\"=1.0.24\"},{\"kind\":\"dev\",\"name\":\"ref-cast-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.81\"}],\"features\":{}}", + "redox_users_0.5.2": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"std\",\"call\"],\"name\":\"libredox\",\"req\":\"^0.1.3\"},{\"name\":\"rust-argon2\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"thiserror\",\"req\":\"^2.0\"},{\"features\":[\"zeroize_derive\"],\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.4\"}],\"features\":{\"auth\":[\"rust-argon2\",\"zeroize\"],\"default\":[\"auth\"]}}", + "ref-cast-impl_1.0.25": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1\"},{\"name\":\"syn\",\"req\":\"^2.0.46\"}],\"features\":{}}", + "ref-cast_1.0.25": "{\"dependencies\":[{\"name\":\"ref-cast-impl\",\"req\":\"=1.0.25\"},{\"kind\":\"dev\",\"name\":\"ref-cast-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{}}", "regex-automata_0.4.13": "{\"dependencies\":[{\"default_features\":false,\"name\":\"aho-corasick\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.69\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"bstr\",\"req\":\"^1.3.0\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.3\"},{\"default_features\":false,\"features\":[\"atty\",\"humantime\",\"termcolor\"],\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.9.3\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.14\"},{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.6.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"default_features\":false,\"name\":\"regex-syntax\",\"optional\":true,\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"regex-test\",\"req\":\"^0.1.0\"}],\"features\":{\"alloc\":[],\"default\":[\"std\",\"syntax\",\"perf\",\"unicode\",\"meta\",\"nfa\",\"dfa\",\"hybrid\"],\"dfa\":[\"dfa-build\",\"dfa-search\",\"dfa-onepass\"],\"dfa-build\":[\"nfa-thompson\",\"dfa-search\"],\"dfa-onepass\":[\"nfa-thompson\"],\"dfa-search\":[],\"hybrid\":[\"alloc\",\"nfa-thompson\"],\"internal-instrument\":[\"internal-instrument-pikevm\"],\"internal-instrument-pikevm\":[\"logging\",\"std\"],\"logging\":[\"dep:log\",\"aho-corasick?/logging\",\"memchr?/logging\"],\"meta\":[\"syntax\",\"nfa-pikevm\"],\"nfa\":[\"nfa-thompson\",\"nfa-pikevm\",\"nfa-backtrack\"],\"nfa-backtrack\":[\"nfa-thompson\"],\"nfa-pikevm\":[\"nfa-thompson\"],\"nfa-thompson\":[\"alloc\"],\"perf\":[\"perf-inline\",\"perf-literal\"],\"perf-inline\":[],\"perf-literal\":[\"perf-literal-substring\",\"perf-literal-multisubstring\"],\"perf-literal-multisubstring\":[\"dep:aho-corasick\"],\"perf-literal-substring\":[\"aho-corasick?/perf-literal\",\"dep:memchr\"],\"std\":[\"regex-syntax?/std\",\"memchr?/std\",\"aho-corasick?/std\",\"alloc\"],\"syntax\":[\"dep:regex-syntax\",\"alloc\"],\"unicode\":[\"unicode-age\",\"unicode-bool\",\"unicode-case\",\"unicode-gencat\",\"unicode-perl\",\"unicode-script\",\"unicode-segment\",\"unicode-word-boundary\",\"regex-syntax?/unicode\"],\"unicode-age\":[\"regex-syntax?/unicode-age\"],\"unicode-bool\":[\"regex-syntax?/unicode-bool\"],\"unicode-case\":[\"regex-syntax?/unicode-case\"],\"unicode-gencat\":[\"regex-syntax?/unicode-gencat\"],\"unicode-perl\":[\"regex-syntax?/unicode-perl\"],\"unicode-script\":[\"regex-syntax?/unicode-script\"],\"unicode-segment\":[\"regex-syntax?/unicode-segment\"],\"unicode-word-boundary\":[]}}", "regex-lite_0.1.8": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.69\"},{\"kind\":\"dev\",\"name\":\"regex-test\",\"req\":\"^0.1.0\"}],\"features\":{\"default\":[\"std\",\"string\"],\"std\":[],\"string\":[]}}", "regex-syntax_0.6.29": "{\"dependencies\":[],\"features\":{\"default\":[\"unicode\"],\"unicode\":[\"unicode-age\",\"unicode-bool\",\"unicode-case\",\"unicode-gencat\",\"unicode-perl\",\"unicode-script\",\"unicode-segment\"],\"unicode-age\":[],\"unicode-bool\":[],\"unicode-case\":[],\"unicode-gencat\":[],\"unicode-perl\":[],\"unicode-script\":[],\"unicode-segment\":[]}}", - "regex-syntax_0.8.5": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.3.0\"}],\"features\":{\"arbitrary\":[\"dep:arbitrary\"],\"default\":[\"std\",\"unicode\"],\"std\":[],\"unicode\":[\"unicode-age\",\"unicode-bool\",\"unicode-case\",\"unicode-gencat\",\"unicode-perl\",\"unicode-script\",\"unicode-segment\"],\"unicode-age\":[],\"unicode-bool\":[],\"unicode-case\":[],\"unicode-gencat\":[],\"unicode-perl\":[],\"unicode-script\":[],\"unicode-segment\":[]}}", + "regex-syntax_0.8.8": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.3.0\"}],\"features\":{\"arbitrary\":[\"dep:arbitrary\"],\"default\":[\"std\",\"unicode\"],\"std\":[],\"unicode\":[\"unicode-age\",\"unicode-bool\",\"unicode-case\",\"unicode-gencat\",\"unicode-perl\",\"unicode-script\",\"unicode-segment\"],\"unicode-age\":[],\"unicode-bool\":[],\"unicode-case\":[],\"unicode-gencat\":[],\"unicode-perl\":[],\"unicode-script\":[],\"unicode-segment\":[]}}", "regex_1.12.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"aho-corasick\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.69\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"atty\",\"humantime\",\"termcolor\"],\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.9.3\"},{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.6.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"default_features\":false,\"features\":[\"alloc\",\"syntax\",\"meta\",\"nfa-pikevm\"],\"name\":\"regex-automata\",\"req\":\"^0.4.12\"},{\"default_features\":false,\"name\":\"regex-syntax\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"regex-test\",\"req\":\"^0.1.0\"}],\"features\":{\"default\":[\"std\",\"perf\",\"unicode\",\"regex-syntax/default\"],\"logging\":[\"aho-corasick?/logging\",\"memchr?/logging\",\"regex-automata/logging\"],\"pattern\":[],\"perf\":[\"perf-cache\",\"perf-dfa\",\"perf-onepass\",\"perf-backtrack\",\"perf-inline\",\"perf-literal\"],\"perf-backtrack\":[\"regex-automata/nfa-backtrack\"],\"perf-cache\":[],\"perf-dfa\":[\"regex-automata/hybrid\"],\"perf-dfa-full\":[\"regex-automata/dfa-build\",\"regex-automata/dfa-search\"],\"perf-inline\":[\"regex-automata/perf-inline\"],\"perf-literal\":[\"dep:aho-corasick\",\"dep:memchr\",\"regex-automata/perf-literal\"],\"perf-onepass\":[\"regex-automata/dfa-onepass\"],\"std\":[\"aho-corasick?/std\",\"memchr?/std\",\"regex-automata/std\",\"regex-syntax/std\"],\"unicode\":[\"unicode-age\",\"unicode-bool\",\"unicode-case\",\"unicode-gencat\",\"unicode-perl\",\"unicode-script\",\"unicode-segment\",\"regex-automata/unicode\",\"regex-syntax/unicode\"],\"unicode-age\":[\"regex-automata/unicode-age\",\"regex-syntax/unicode-age\"],\"unicode-bool\":[\"regex-automata/unicode-bool\",\"regex-syntax/unicode-bool\"],\"unicode-case\":[\"regex-automata/unicode-case\",\"regex-syntax/unicode-case\"],\"unicode-gencat\":[\"regex-automata/unicode-gencat\",\"regex-syntax/unicode-gencat\"],\"unicode-perl\":[\"regex-automata/unicode-perl\",\"regex-automata/unicode-word-boundary\",\"regex-syntax/unicode-perl\"],\"unicode-script\":[\"regex-automata/unicode-script\",\"regex-syntax/unicode-script\"],\"unicode-segment\":[\"regex-automata/unicode-segment\",\"regex-syntax/unicode-segment\"],\"unstable\":[\"pattern\"],\"use_std\":[\"std\"]}}", - "reqwest_0.12.24": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"tokio\"],\"name\":\"async-compression\",\"optional\":true,\"req\":\"^0.4.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"brotli_crate\",\"package\":\"brotli\",\"req\":\"^8\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"name\":\"cookie_crate\",\"optional\":true,\"package\":\"cookie\",\"req\":\"^0.18.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"cookie_store\",\"optional\":true,\"req\":\"^0.21.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0.13\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.28\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.28\"},{\"default_features\":false,\"features\":[\"std\",\"alloc\"],\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.28\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"h3\",\"optional\":true,\"req\":\"^0.0.8\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"h3-quinn\",\"optional\":true,\"req\":\"^0.0.10\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"tokio\"],\"name\":\"hickory-resolver\",\"optional\":true,\"req\":\"^0.25\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"http\",\"req\":\"^1.1\"},{\"name\":\"http-body\",\"req\":\"^1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"http-body-util\",\"req\":\"^0.1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"http1\",\"client\"],\"name\":\"hyper\",\"req\":\"^1.1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"http1\",\"http2\",\"client\",\"server\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"http1\",\"tls12\"],\"name\":\"hyper-rustls\",\"optional\":true,\"req\":\"^0.27.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"hyper-tls\",\"optional\":true,\"req\":\"^0.6\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"http1\",\"client\",\"client-legacy\",\"client-proxy\",\"tokio\"],\"name\":\"hyper-util\",\"req\":\"^0.1.12\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"http1\",\"http2\",\"client\",\"client-legacy\",\"server-auto\",\"server-graceful\",\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1.12\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"js-sys\",\"req\":\"^0.3.77\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0\"},{\"name\":\"log\",\"req\":\"^0.4.17\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"mime\",\"optional\":true,\"req\":\"^0.3.16\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0\"},{\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\",\"req\":\"^0.2.10\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.0\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.18\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"percent-encoding\",\"req\":\"^2.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"rustls\",\"runtime-tokio\"],\"name\":\"quinn\",\"optional\":true,\"req\":\"^0.11.1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"std\",\"tls12\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.4\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"std\"],\"name\":\"rustls-pki-types\",\"optional\":true,\"req\":\"^1.9.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"serde_json\",\"req\":\"^1.0\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_urlencoded\",\"req\":\"^0.7.1\"},{\"features\":[\"futures\"],\"name\":\"sync_wrapper\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"net\",\"time\"],\"name\":\"tokio\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"tokio-native-tls\",\"optional\":true,\"req\":\"^0.3.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"tls12\"],\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"codec\",\"io\"],\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7.9\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"retry\",\"timeout\",\"util\"],\"name\":\"tower\",\"req\":\"^0.5.2\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"limit\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"default_features\":false,\"features\":[\"follow-redirect\"],\"name\":\"tower-http\",\"req\":\"^0.6.5\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"tower-service\",\"req\":\"^0.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"url\",\"req\":\"^2.4\"},{\"name\":\"wasm-bindgen\",\"req\":\"^0.2.89\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"features\":[\"serde-serialize\"],\"kind\":\"dev\",\"name\":\"wasm-bindgen\",\"req\":\"^0.2.89\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"wasm-bindgen-futures\",\"req\":\"^0.4.18\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"wasm-streams\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"features\":[\"AbortController\",\"AbortSignal\",\"Headers\",\"Request\",\"RequestInit\",\"RequestMode\",\"Response\",\"Window\",\"FormData\",\"Blob\",\"BlobPropertyBag\",\"ServiceWorkerGlobalScope\",\"RequestCredentials\",\"File\",\"ReadableStream\",\"RequestCache\"],\"name\":\"web-sys\",\"req\":\"^0.3.28\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"zstd_crate\",\"package\":\"zstd\",\"req\":\"^0.13\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"}],\"features\":{\"__rustls\":[\"dep:hyper-rustls\",\"dep:tokio-rustls\",\"dep:rustls\",\"__tls\"],\"__rustls-ring\":[\"hyper-rustls?/ring\",\"tokio-rustls?/ring\",\"rustls?/ring\",\"quinn?/ring\"],\"__tls\":[\"dep:rustls-pki-types\",\"tokio/io-util\"],\"blocking\":[\"dep:futures-channel\",\"futures-channel?/sink\",\"dep:futures-util\",\"futures-util?/io\",\"futures-util?/sink\",\"tokio/sync\"],\"brotli\":[\"dep:async-compression\",\"async-compression?/brotli\",\"dep:futures-util\",\"dep:tokio-util\"],\"charset\":[\"dep:encoding_rs\",\"dep:mime\"],\"cookies\":[\"dep:cookie_crate\",\"dep:cookie_store\"],\"default\":[\"default-tls\",\"charset\",\"http2\",\"system-proxy\"],\"default-tls\":[\"dep:hyper-tls\",\"dep:native-tls-crate\",\"__tls\",\"dep:tokio-native-tls\"],\"deflate\":[\"dep:async-compression\",\"async-compression?/zlib\",\"dep:futures-util\",\"dep:tokio-util\"],\"gzip\":[\"dep:async-compression\",\"async-compression?/gzip\",\"dep:futures-util\",\"dep:tokio-util\"],\"hickory-dns\":[\"dep:hickory-resolver\",\"dep:once_cell\"],\"http2\":[\"h2\",\"hyper/http2\",\"hyper-util/http2\",\"hyper-rustls?/http2\"],\"http3\":[\"rustls-tls-manual-roots\",\"dep:h3\",\"dep:h3-quinn\",\"dep:quinn\",\"tokio/macros\"],\"json\":[\"dep:serde_json\"],\"macos-system-configuration\":[\"system-proxy\"],\"multipart\":[\"dep:mime_guess\",\"dep:futures-util\"],\"native-tls\":[\"default-tls\"],\"native-tls-alpn\":[\"native-tls\",\"native-tls-crate?/alpn\",\"hyper-tls?/alpn\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate?/vendored\"],\"rustls-tls\":[\"rustls-tls-webpki-roots\"],\"rustls-tls-manual-roots\":[\"rustls-tls-manual-roots-no-provider\",\"__rustls-ring\"],\"rustls-tls-manual-roots-no-provider\":[\"__rustls\"],\"rustls-tls-native-roots\":[\"rustls-tls-native-roots-no-provider\",\"__rustls-ring\"],\"rustls-tls-native-roots-no-provider\":[\"dep:rustls-native-certs\",\"hyper-rustls?/native-tokio\",\"__rustls\"],\"rustls-tls-no-provider\":[\"rustls-tls-manual-roots-no-provider\"],\"rustls-tls-webpki-roots\":[\"rustls-tls-webpki-roots-no-provider\",\"__rustls-ring\"],\"rustls-tls-webpki-roots-no-provider\":[\"dep:webpki-roots\",\"hyper-rustls?/webpki-tokio\",\"__rustls\"],\"socks\":[],\"stream\":[\"tokio/fs\",\"dep:futures-util\",\"dep:tokio-util\",\"dep:wasm-streams\"],\"system-proxy\":[\"hyper-util/client-proxy-system\"],\"trust-dns\":[],\"zstd\":[\"dep:async-compression\",\"async-compression?/zstd\",\"dep:futures-util\",\"dep:tokio-util\"]}}", + "reqwest_0.12.28": "{\"dependencies\":[{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"brotli_crate\",\"package\":\"brotli\",\"req\":\"^8\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"name\":\"cookie_crate\",\"optional\":true,\"package\":\"cookie\",\"req\":\"^0.18.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"cookie_store\",\"optional\":true,\"req\":\"^0.22.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0.13\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"futures-channel\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.28\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.28\"},{\"default_features\":false,\"features\":[\"std\",\"alloc\"],\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.28\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"h3\",\"optional\":true,\"req\":\"^0.0.8\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"h3-quinn\",\"optional\":true,\"req\":\"^0.0.10\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"tokio\"],\"name\":\"hickory-resolver\",\"optional\":true,\"req\":\"^0.25\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"http\",\"req\":\"^1.1\"},{\"name\":\"http-body\",\"req\":\"^1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"http-body-util\",\"req\":\"^0.1.2\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"http1\",\"client\"],\"name\":\"hyper\",\"req\":\"^1.1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"http1\",\"http2\",\"client\",\"server\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"http1\",\"tls12\"],\"name\":\"hyper-rustls\",\"optional\":true,\"req\":\"^0.27.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"hyper-tls\",\"optional\":true,\"req\":\"^0.6\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"http1\",\"client\",\"client-legacy\",\"client-proxy\",\"tokio\"],\"name\":\"hyper-util\",\"req\":\"^0.1.12\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"http1\",\"http2\",\"client\",\"client-legacy\",\"server-auto\",\"server-graceful\",\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1.12\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"js-sys\",\"req\":\"^0.3.77\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0\"},{\"name\":\"log\",\"req\":\"^0.4.17\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"mime\",\"optional\":true,\"req\":\"^0.3.16\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0\"},{\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\",\"req\":\"^0.2.10\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.0\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.18\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"percent-encoding\",\"req\":\"^2.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"rustls\",\"runtime-tokio\"],\"name\":\"quinn\",\"optional\":true,\"req\":\"^0.11.1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"std\",\"tls12\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.4\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"std\"],\"name\":\"rustls-pki-types\",\"optional\":true,\"req\":\"^1.9.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"serde_json\",\"req\":\"^1.0\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_urlencoded\",\"req\":\"^0.7.1\"},{\"features\":[\"futures\"],\"name\":\"sync_wrapper\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"net\",\"time\"],\"name\":\"tokio\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"tokio-native-tls\",\"optional\":true,\"req\":\"^0.3.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"tls12\"],\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"io\"],\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7.9\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"retry\",\"timeout\",\"util\"],\"name\":\"tower\",\"req\":\"^0.5.2\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"limit\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"default_features\":false,\"features\":[\"follow-redirect\"],\"name\":\"tower-http\",\"req\":\"^0.6.8\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"tower-service\",\"req\":\"^0.3\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"name\":\"url\",\"req\":\"^2.4\"},{\"name\":\"wasm-bindgen\",\"req\":\"^0.2.89\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"features\":[\"serde-serialize\"],\"kind\":\"dev\",\"name\":\"wasm-bindgen\",\"req\":\"^0.2.89\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"wasm-bindgen-futures\",\"req\":\"^0.4.18\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"wasm-streams\",\"optional\":true,\"req\":\"^0.4\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"features\":[\"AbortController\",\"AbortSignal\",\"Headers\",\"Request\",\"RequestInit\",\"RequestMode\",\"Response\",\"Window\",\"FormData\",\"Blob\",\"BlobPropertyBag\",\"ServiceWorkerGlobalScope\",\"RequestCredentials\",\"File\",\"ReadableStream\",\"RequestCache\"],\"name\":\"web-sys\",\"req\":\"^0.3.28\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^1\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"kind\":\"dev\",\"name\":\"zstd_crate\",\"package\":\"zstd\",\"req\":\"^0.13\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"}],\"features\":{\"__rustls\":[\"dep:hyper-rustls\",\"dep:tokio-rustls\",\"dep:rustls\",\"__tls\"],\"__rustls-ring\":[\"hyper-rustls?/ring\",\"tokio-rustls?/ring\",\"rustls?/ring\",\"quinn?/ring\"],\"__tls\":[\"dep:rustls-pki-types\",\"tokio/io-util\"],\"blocking\":[\"dep:futures-channel\",\"futures-channel?/sink\",\"dep:futures-util\",\"futures-util?/io\",\"futures-util?/sink\",\"tokio/sync\"],\"brotli\":[\"tower-http/decompression-br\"],\"charset\":[\"dep:encoding_rs\",\"dep:mime\"],\"cookies\":[\"dep:cookie_crate\",\"dep:cookie_store\"],\"default\":[\"default-tls\",\"charset\",\"http2\",\"system-proxy\"],\"default-tls\":[\"dep:hyper-tls\",\"dep:native-tls-crate\",\"__tls\",\"dep:tokio-native-tls\"],\"deflate\":[\"tower-http/decompression-deflate\"],\"gzip\":[\"tower-http/decompression-gzip\"],\"hickory-dns\":[\"dep:hickory-resolver\",\"dep:once_cell\"],\"http2\":[\"h2\",\"hyper/http2\",\"hyper-util/http2\",\"hyper-rustls?/http2\"],\"http3\":[\"rustls-tls-manual-roots\",\"dep:h3\",\"dep:h3-quinn\",\"dep:quinn\",\"tokio/macros\"],\"json\":[\"dep:serde_json\"],\"macos-system-configuration\":[\"system-proxy\"],\"multipart\":[\"dep:mime_guess\",\"dep:futures-util\"],\"native-tls\":[\"default-tls\"],\"native-tls-alpn\":[\"native-tls\",\"native-tls-crate?/alpn\",\"hyper-tls?/alpn\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate?/vendored\"],\"rustls-tls\":[\"rustls-tls-webpki-roots\"],\"rustls-tls-manual-roots\":[\"rustls-tls-manual-roots-no-provider\",\"__rustls-ring\"],\"rustls-tls-manual-roots-no-provider\":[\"__rustls\"],\"rustls-tls-native-roots\":[\"rustls-tls-native-roots-no-provider\",\"__rustls-ring\"],\"rustls-tls-native-roots-no-provider\":[\"dep:rustls-native-certs\",\"hyper-rustls?/native-tokio\",\"__rustls\"],\"rustls-tls-no-provider\":[\"rustls-tls-manual-roots-no-provider\"],\"rustls-tls-webpki-roots\":[\"rustls-tls-webpki-roots-no-provider\",\"__rustls-ring\"],\"rustls-tls-webpki-roots-no-provider\":[\"dep:webpki-roots\",\"hyper-rustls?/webpki-tokio\",\"__rustls\"],\"socks\":[],\"stream\":[\"tokio/fs\",\"dep:futures-util\",\"dep:tokio-util\",\"dep:wasm-streams\"],\"system-proxy\":[\"hyper-util/client-proxy-system\"],\"trust-dns\":[],\"zstd\":[\"tower-http/decompression-zstd\"]}}", "resolv-conf_0.7.6": "{\"dependencies\":[],\"features\":{\"system\":[]}}", "ring_0.17.14": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.2.8\"},{\"default_features\":false,\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"getrandom\",\"req\":\"^0.2.10\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.148\",\"target\":\"cfg(all(any(all(target_arch = \\\"aarch64\\\", target_endian = \\\"little\\\"), all(target_arch = \\\"arm\\\", target_endian = \\\"little\\\")), any(target_os = \\\"android\\\", target_os = \\\"linux\\\")))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"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\\\")))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.148\",\"target\":\"cfg(any(unix, windows, target_os = \\\"wasi\\\"))\"},{\"name\":\"untrusted\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.37\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Threading\"],\"name\":\"windows-sys\",\"req\":\"^0.52\",\"target\":\"cfg(all(all(target_arch = \\\"aarch64\\\", target_endian = \\\"little\\\"), target_os = \\\"windows\\\"))\"}],\"features\":{\"alloc\":[],\"default\":[\"alloc\",\"dev_urandom_fallback\"],\"dev_urandom_fallback\":[],\"less-safe-getrandom-custom-or-rdrand\":[],\"less-safe-getrandom-espidf\":[],\"slow_tests\":[],\"std\":[\"alloc\"],\"test_logging\":[],\"unstable-testing-arm-no-hw\":[],\"unstable-testing-arm-no-neon\":[],\"wasm32_unknown_unknown_js\":[\"getrandom/js\"]}}", "rmcp-macros_0.12.0": "{\"dependencies\":[{\"name\":\"darling\",\"req\":\"^0.23\"},{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", "rmcp_0.12.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"async-trait\",\"req\":\"^0.1.89\"},{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"axum\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"serde\",\"clock\",\"std\",\"oldtime\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"features\":[\"serde\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"http\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"oauth2\",\"optional\":true,\"req\":\"^5.0\"},{\"name\":\"pastey\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"features\":[\"tokio1\"],\"name\":\"process-wrap\",\"optional\":true,\"req\":\"^9.0\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"json\",\"stream\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"rmcp-macros\",\"optional\":true,\"req\":\"^0.12.0\"},{\"features\":[\"chrono04\"],\"name\":\"schemars\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"chrono04\"],\"kind\":\"dev\",\"name\":\"schemars\",\"req\":\"^1.1.0\"},{\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"sse-stream\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"sync\",\"macros\",\"rt\",\"time\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"name\":\"tower-service\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2.4\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"__reqwest\":[\"dep:reqwest\"],\"auth\":[\"dep:oauth2\",\"__reqwest\",\"dep:url\"],\"client\":[\"dep:tokio-stream\"],\"client-side-sse\":[\"dep:sse-stream\",\"dep:http\"],\"default\":[\"base64\",\"macros\",\"server\"],\"elicitation\":[],\"macros\":[\"dep:rmcp-macros\",\"dep:pastey\"],\"reqwest\":[\"__reqwest\",\"reqwest?/rustls-tls\"],\"reqwest-tls-no-provider\":[\"__reqwest\",\"reqwest?/rustls-tls-no-provider\"],\"schemars\":[\"dep:schemars\"],\"server\":[\"transport-async-rw\",\"dep:schemars\"],\"server-side-http\":[\"uuid\",\"dep:rand\",\"dep:tokio-stream\",\"dep:http\",\"dep:http-body\",\"dep:http-body-util\",\"dep:bytes\",\"dep:sse-stream\",\"tower\"],\"tower\":[\"dep:tower-service\"],\"transport-async-rw\":[\"tokio/io-util\",\"tokio-util/codec\"],\"transport-child-process\":[\"transport-async-rw\",\"tokio/process\",\"dep:process-wrap\"],\"transport-io\":[\"transport-async-rw\",\"tokio/io-std\"],\"transport-streamable-http-client\":[\"client-side-sse\",\"transport-worker\"],\"transport-streamable-http-client-reqwest\":[\"transport-streamable-http-client\",\"reqwest\"],\"transport-streamable-http-server\":[\"transport-streamable-http-server-session\",\"server-side-http\",\"transport-worker\"],\"transport-streamable-http-server-session\":[\"transport-async-rw\",\"dep:tokio-stream\"],\"transport-worker\":[\"dep:tokio-stream\"]}}", "rsa_0.9.10": "{\"dependencies\":[{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"base64ct\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"const-oid\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"alloc\",\"oid\"],\"name\":\"digest\",\"req\":\"^0.10.5\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4.1\"},{\"default_features\":false,\"features\":[\"i128\",\"prime\",\"zeroize\"],\"name\":\"num-bigint\",\"package\":\"num-bigint-dig\",\"req\":\"^0.8.6\"},{\"default_features\":false,\"name\":\"num-integer\",\"req\":\"^0.1.39\"},{\"default_features\":false,\"features\":[\"libm\"],\"name\":\"num-traits\",\"req\":\"^0.2.9\"},{\"default_features\":false,\"features\":[\"alloc\",\"pkcs8\"],\"name\":\"pkcs1\",\"req\":\"^0.7.5\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"pkcs8\",\"req\":\"^0.10.2\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rand_chacha\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"rand_core\",\"req\":\"^0.6.4\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.184\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.89\"},{\"default_features\":false,\"features\":[\"oid\"],\"name\":\"sha1\",\"optional\":true,\"req\":\"^0.10.5\"},{\"default_features\":false,\"features\":[\"oid\"],\"kind\":\"dev\",\"name\":\"sha1\",\"req\":\"^0.10.5\"},{\"default_features\":false,\"features\":[\"oid\"],\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10.6\"},{\"default_features\":false,\"features\":[\"oid\"],\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10.6\"},{\"default_features\":false,\"features\":[\"oid\"],\"kind\":\"dev\",\"name\":\"sha3\",\"req\":\"^0.10.7\"},{\"default_features\":false,\"features\":[\"alloc\",\"digest\",\"rand_core\"],\"name\":\"signature\",\"req\":\">2.0, <2.3\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"spki\",\"req\":\"^0.7.3\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2.1.1\"},{\"features\":[\"alloc\"],\"name\":\"zeroize\",\"req\":\"^1.5\"}],\"features\":{\"default\":[\"std\",\"pem\",\"u64_digit\"],\"getrandom\":[\"rand_core/getrandom\"],\"hazmat\":[],\"nightly\":[\"num-bigint/nightly\"],\"pem\":[\"pkcs1/pem\",\"pkcs8/pem\"],\"pkcs5\":[\"pkcs8/encryption\"],\"serde\":[\"dep:serde\",\"num-bigint/serde\"],\"std\":[\"digest/std\",\"pkcs1/std\",\"pkcs8/std\",\"rand_core/std\",\"signature/std\"],\"u64_digit\":[\"num-bigint/u64_digit\"]}}", - "rustc-demangle_0.1.25": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"compiler_builtins\":[],\"rustc-dep-of-std\":[\"core\"],\"std\":[]}}", + "rust-embed-impl_8.11.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"name\":\"rust-embed-utils\",\"req\":\"^8.11.0\"},{\"name\":\"shellexpand\",\"optional\":true,\"req\":\"^3\"},{\"default_features\":false,\"features\":[\"derive\",\"parsing\",\"proc-macro\",\"printing\"],\"name\":\"syn\",\"req\":\"^2\"},{\"name\":\"walkdir\",\"req\":\"^2.3.1\"}],\"features\":{\"compression\":[],\"debug-embed\":[],\"deterministic-timestamps\":[],\"include-exclude\":[\"rust-embed-utils/include-exclude\"],\"interpolate-folder-path\":[\"shellexpand\"],\"mime-guess\":[\"rust-embed-utils/mime-guess\"]}}", + "rust-embed-utils_8.11.0": "{\"dependencies\":[{\"name\":\"globset\",\"optional\":true,\"req\":\"^0.4.8\"},{\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0.4\"},{\"name\":\"sha2\",\"req\":\"^0.10.5\"},{\"name\":\"walkdir\",\"req\":\"^2.3.1\"}],\"features\":{\"debug-embed\":[],\"include-exclude\":[\"globset\"],\"mime-guess\":[\"mime_guess\"]}}", + "rust-embed_8.11.0": "{\"dependencies\":[{\"name\":\"actix-web\",\"optional\":true,\"req\":\"^4\"},{\"default_features\":false,\"features\":[\"http1\",\"tokio\"],\"name\":\"axum\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"hex\",\"optional\":true,\"req\":\"^0.4.3\"},{\"name\":\"include-flate\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0.5\"},{\"default_features\":false,\"features\":[\"server\"],\"name\":\"poem\",\"optional\":true,\"req\":\"^1.3.30\"},{\"default_features\":false,\"name\":\"rocket\",\"optional\":true,\"req\":\"^0.5.0-rc.2\"},{\"name\":\"rust-embed-impl\",\"req\":\"^8.9.0\"},{\"name\":\"rust-embed-utils\",\"req\":\"^8.9.0\"},{\"default_features\":false,\"name\":\"salvo\",\"optional\":true,\"req\":\"^0.16\"},{\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"walkdir\",\"req\":\"^2.3.2\"},{\"default_features\":false,\"name\":\"warp\",\"optional\":true,\"req\":\"^0.3\"}],\"features\":{\"actix\":[\"actix-web\",\"mime_guess\"],\"axum-ex\":[\"axum\",\"tokio\",\"mime_guess\"],\"compression\":[\"rust-embed-impl/compression\",\"include-flate\"],\"debug-embed\":[\"rust-embed-impl/debug-embed\",\"rust-embed-utils/debug-embed\"],\"deterministic-timestamps\":[\"rust-embed-impl/deterministic-timestamps\"],\"include-exclude\":[\"rust-embed-impl/include-exclude\",\"rust-embed-utils/include-exclude\"],\"interpolate-folder-path\":[\"rust-embed-impl/interpolate-folder-path\"],\"mime-guess\":[\"rust-embed-impl/mime-guess\",\"rust-embed-utils/mime-guess\"],\"poem-ex\":[\"poem\",\"tokio\",\"mime_guess\",\"hex\"],\"salvo-ex\":[\"salvo\",\"tokio\",\"mime_guess\",\"hex\"],\"warp-ex\":[\"warp\",\"tokio\",\"mime_guess\"]}}", + "rustc-demangle_0.1.27": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"}],\"features\":{\"compiler_builtins\":[],\"rustc-dep-of-std\":[\"core\"],\"std\":[]}}", + "rustc-hash_1.1.0": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "rustc-hash_2.1.1": "{\"dependencies\":[{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.8\"}],\"features\":{\"default\":[\"std\"],\"nightly\":[],\"rand\":[\"dep:rand\",\"std\"],\"std\":[]}}", "rustc_version_0.4.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"semver\",\"req\":\"^1.0\"}],\"features\":{}}", "rustix_0.38.44": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bitflags\",\"req\":\"^2.4.0\"},{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.49\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\",\"target\":\"cfg(all(criterion, not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"itoa\",\"optional\":true,\"req\":\"^1.0.13\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.161\",\"target\":\"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", target_arch = \\\"s390x\\\"), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.161\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", target_arch = \\\"s390x\\\"), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.161\"},{\"default_features\":false,\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", target_arch = \\\"s390x\\\"), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(windows)\"},{\"default_features\":false,\"name\":\"libc_errno\",\"optional\":true,\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", target_arch = \\\"s390x\\\"), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\"},{\"default_features\":false,\"features\":[\"general\",\"ioctl\",\"no_std\"],\"name\":\"linux-raw-sys\",\"req\":\"^0.4.14\",\"target\":\"cfg(all(any(target_os = \\\"android\\\", target_os = \\\"linux\\\"), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", target_arch = \\\"s390x\\\"), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"features\":[\"general\",\"errno\",\"ioctl\",\"no_std\",\"elf\"],\"name\":\"linux-raw-sys\",\"req\":\"^0.4.14\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", target_arch = \\\"s390x\\\"), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"kind\":\"dev\",\"name\":\"memoffset\",\"req\":\"^0.9.0\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.5.2\",\"target\":\"cfg(any(target_os = \\\"android\\\", target_os = \\\"linux\\\"))\"},{\"name\":\"rustc-std-workspace-alloc\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.5.0\"},{\"features\":[\"Win32_Foundation\",\"Win32_Networking_WinSock\",\"Win32_NetworkManagement_IpHelper\",\"Win32_System_Threading\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <=0.59\",\"target\":\"cfg(windows)\"}],\"features\":{\"all-apis\":[\"event\",\"fs\",\"io_uring\",\"mm\",\"mount\",\"net\",\"param\",\"pipe\",\"process\",\"procfs\",\"pty\",\"rand\",\"runtime\",\"shm\",\"stdio\",\"system\",\"termios\",\"thread\",\"time\"],\"alloc\":[],\"cc\":[],\"default\":[\"std\",\"use-libc-auxv\"],\"event\":[],\"fs\":[],\"io_uring\":[\"event\",\"fs\",\"net\",\"linux-raw-sys/io_uring\"],\"libc-extra-traits\":[\"libc?/extra_traits\"],\"linux_4_11\":[],\"linux_latest\":[\"linux_4_11\"],\"mm\":[],\"mount\":[],\"net\":[\"linux-raw-sys/net\",\"linux-raw-sys/netlink\",\"linux-raw-sys/if_ether\",\"linux-raw-sys/xdp\"],\"param\":[\"fs\"],\"pipe\":[],\"process\":[\"linux-raw-sys/prctl\"],\"procfs\":[\"once_cell\",\"itoa\",\"fs\"],\"pty\":[\"itoa\",\"fs\"],\"rand\":[],\"runtime\":[\"linux-raw-sys/prctl\"],\"rustc-dep-of-std\":[\"core\",\"rustc-std-workspace-alloc\",\"compiler_builtins\",\"linux-raw-sys/rustc-dep-of-std\",\"bitflags/rustc-dep-of-std\",\"compiler_builtins?/rustc-dep-of-std\"],\"shm\":[\"fs\"],\"std\":[\"bitflags/std\",\"alloc\",\"libc?/std\",\"libc_errno?/std\",\"libc-extra-traits\"],\"stdio\":[],\"system\":[\"linux-raw-sys/system\"],\"termios\":[],\"thread\":[\"linux-raw-sys/prctl\"],\"time\":[],\"try_close\":[],\"use-explicitly-provided-auxv\":[],\"use-libc\":[\"libc_errno\",\"libc\",\"libc-extra-traits\"],\"use-libc-auxv\":[]}}", - "rustix_1.0.8": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bitflags\",\"req\":\"^2.4.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\",\"target\":\"cfg(all(criterion, not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.168\",\"target\":\"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.168\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.168\"},{\"default_features\":false,\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(windows)\"},{\"default_features\":false,\"name\":\"libc_errno\",\"optional\":true,\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\"},{\"default_features\":false,\"features\":[\"general\",\"ioctl\",\"no_std\"],\"name\":\"linux-raw-sys\",\"req\":\"^0.9.2\",\"target\":\"cfg(all(any(target_os = \\\"android\\\", target_os = \\\"linux\\\"), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"features\":[\"general\",\"errno\",\"ioctl\",\"no_std\",\"elf\"],\"name\":\"linux-raw-sys\",\"req\":\"^0.9.2\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"kind\":\"dev\",\"name\":\"memoffset\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.20.3\",\"target\":\"cfg(windows)\"},{\"name\":\"rustc-std-workspace-alloc\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.5.0\"},{\"features\":[\"Win32_Foundation\",\"Win32_Networking_WinSock\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"all-apis\":[\"event\",\"fs\",\"io_uring\",\"mm\",\"mount\",\"net\",\"param\",\"pipe\",\"process\",\"pty\",\"rand\",\"runtime\",\"shm\",\"stdio\",\"system\",\"termios\",\"thread\",\"time\"],\"alloc\":[],\"default\":[\"std\"],\"event\":[],\"fs\":[],\"io_uring\":[\"event\",\"fs\",\"net\",\"thread\",\"linux-raw-sys/io_uring\"],\"linux_4_11\":[],\"linux_5_1\":[\"linux_4_11\"],\"linux_5_11\":[\"linux_5_1\"],\"linux_latest\":[\"linux_5_11\"],\"mm\":[],\"mount\":[],\"net\":[\"linux-raw-sys/net\",\"linux-raw-sys/netlink\",\"linux-raw-sys/if_ether\",\"linux-raw-sys/xdp\"],\"param\":[],\"pipe\":[],\"process\":[\"linux-raw-sys/prctl\"],\"pty\":[\"fs\"],\"rand\":[],\"runtime\":[\"linux-raw-sys/prctl\"],\"rustc-dep-of-std\":[\"core\",\"rustc-std-workspace-alloc\",\"linux-raw-sys/rustc-dep-of-std\",\"bitflags/rustc-dep-of-std\"],\"shm\":[\"fs\"],\"std\":[\"bitflags/std\",\"alloc\",\"libc?/std\",\"libc_errno?/std\"],\"stdio\":[],\"system\":[\"linux-raw-sys/system\"],\"termios\":[],\"thread\":[\"linux-raw-sys/prctl\"],\"time\":[],\"try_close\":[],\"use-explicitly-provided-auxv\":[],\"use-libc\":[\"libc_errno\",\"libc\"],\"use-libc-auxv\":[]}}", - "rustls-native-certs_0.8.1": "{\"dependencies\":[{\"name\":\"openssl-probe\",\"req\":\"^0.1.2\",\"target\":\"cfg(all(unix, not(target_os = \\\"macos\\\")))\"},{\"features\":[\"std\"],\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.10\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17\"},{\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"kind\":\"dev\",\"name\":\"rustls-webpki\",\"req\":\"^0.102\"},{\"name\":\"schannel\",\"req\":\"^0.1\",\"target\":\"cfg(windows)\"},{\"name\":\"security-framework\",\"req\":\"^3\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.5\"},{\"kind\":\"dev\",\"name\":\"untrusted\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"webpki-roots\",\"req\":\"^0.26\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.16\"}],\"features\":{}}", - "rustls-pki-types_1.12.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"crabgrind\",\"req\":\"=0.1.9\",\"target\":\"cfg(all(target_os = \\\"linux\\\", target_arch = \\\"x86_64\\\"))\"},{\"name\":\"web-time\",\"optional\":true,\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[\"dep:zeroize\"],\"default\":[\"alloc\"],\"std\":[\"alloc\"],\"web\":[\"web-time\"]}}", - "rustls-webpki_0.103.4": "{\"dependencies\":[{\"default_features\":false,\"name\":\"aws-lc-rs\",\"optional\":true,\"req\":\"^1.9\"},{\"kind\":\"dev\",\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"kind\":\"dev\",\"name\":\"bzip2\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.17.2\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.12\"},{\"default_features\":false,\"features\":[\"aws_lc_rs\"],\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"ring\",\"optional\":true,\"req\":\"^0.17\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"untrusted\",\"req\":\"^0.9\"}],\"features\":{\"alloc\":[\"ring?/alloc\",\"pki-types/alloc\"],\"aws-lc-rs\":[\"dep:aws-lc-rs\",\"aws-lc-rs/aws-lc-sys\",\"aws-lc-rs/prebuilt-nasm\"],\"aws-lc-rs-fips\":[\"dep:aws-lc-rs\",\"aws-lc-rs/fips\"],\"aws-lc-rs-unstable\":[\"aws-lc-rs\",\"aws-lc-rs/unstable\"],\"default\":[\"std\"],\"ring\":[\"dep:ring\"],\"std\":[\"alloc\",\"pki-types/std\"]}}", - "rustls_0.23.29": "{\"dependencies\":[{\"default_features\":false,\"name\":\"aws-lc-rs\",\"optional\":true,\"req\":\"^1.12\"},{\"kind\":\"dev\",\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"brotli\",\"optional\":true,\"req\":\"^8\"},{\"name\":\"brotli-decompressor\",\"optional\":true,\"req\":\"^5.0.0\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"default_features\":false,\"features\":[\"default-hasher\",\"inline-more\"],\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.8\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.8\"},{\"kind\":\"dev\",\"name\":\"macro_rules_attribute\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"num-bigint\",\"req\":\"^0.4.4\"},{\"default_features\":false,\"features\":[\"alloc\",\"race\"],\"name\":\"once_cell\",\"req\":\"^1.16\"},{\"features\":[\"alloc\"],\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.12\"},{\"default_features\":false,\"features\":[\"pem\",\"aws_lc_rs\"],\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"name\":\"ring\",\"optional\":true,\"req\":\"^0.17\"},{\"kind\":\"build\",\"name\":\"rustversion\",\"optional\":true,\"req\":\"^1.0.6\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.6\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103.4\"},{\"kind\":\"dev\",\"name\":\"webpki-roots\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17\"},{\"name\":\"zeroize\",\"req\":\"^1.7\"},{\"name\":\"zlib-rs\",\"optional\":true,\"req\":\"^0.5\"}],\"features\":{\"aws-lc-rs\":[\"aws_lc_rs\"],\"aws_lc_rs\":[\"dep:aws-lc-rs\",\"webpki/aws-lc-rs\",\"aws-lc-rs/aws-lc-sys\",\"aws-lc-rs/prebuilt-nasm\"],\"brotli\":[\"dep:brotli\",\"dep:brotli-decompressor\",\"std\"],\"custom-provider\":[],\"default\":[\"aws_lc_rs\",\"logging\",\"prefer-post-quantum\",\"std\",\"tls12\"],\"fips\":[\"aws_lc_rs\",\"aws-lc-rs?/fips\",\"webpki/aws-lc-rs-fips\"],\"logging\":[\"log\"],\"prefer-post-quantum\":[\"aws_lc_rs\"],\"read_buf\":[\"rustversion\",\"std\"],\"ring\":[\"dep:ring\",\"webpki/ring\"],\"std\":[\"webpki/std\",\"pki-types/std\",\"once_cell/std\"],\"tls12\":[],\"zlib\":[\"dep:zlib-rs\"]}}", - "rustversion_1.0.21": "{\"dependencies\":[{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.49\"}],\"features\":{}}", + "rustix_1.1.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bitflags\",\"req\":\"^2.4.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\",\"target\":\"cfg(all(criterion, not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.177\",\"target\":\"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.177\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.171\"},{\"default_features\":false,\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(windows)\"},{\"default_features\":false,\"name\":\"libc_errno\",\"optional\":true,\"package\":\"errno\",\"req\":\"^0.3.10\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc_errno\",\"package\":\"errno\",\"req\":\"^0.3.10\"},{\"default_features\":false,\"features\":[\"general\",\"ioctl\",\"no_std\"],\"name\":\"linux-raw-sys\",\"req\":\"^0.11.0\",\"target\":\"cfg(all(any(target_os = \\\"linux\\\", target_os = \\\"android\\\"), any(rustix_use_libc, miri, not(all(target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\")))))))\"},{\"default_features\":false,\"features\":[\"auxvec\",\"general\",\"errno\",\"ioctl\",\"no_std\",\"elf\"],\"name\":\"linux-raw-sys\",\"req\":\"^0.11.0\",\"target\":\"cfg(all(not(rustix_use_libc), not(miri), target_os = \\\"linux\\\", any(target_endian = \\\"little\\\", any(target_arch = \\\"s390x\\\", target_arch = \\\"powerpc\\\")), any(target_arch = \\\"arm\\\", all(target_arch = \\\"aarch64\\\", target_pointer_width = \\\"64\\\"), target_arch = \\\"riscv64\\\", all(rustix_use_experimental_asm, target_arch = \\\"powerpc\\\"), all(rustix_use_experimental_asm, target_arch = \\\"powerpc64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"s390x\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips32r6\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64\\\"), all(rustix_use_experimental_asm, target_arch = \\\"mips64r6\\\"), target_arch = \\\"x86\\\", all(target_arch = \\\"x86_64\\\", target_pointer_width = \\\"64\\\"))))\"},{\"kind\":\"dev\",\"name\":\"memoffset\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.20.3\",\"target\":\"cfg(windows)\"},{\"name\":\"rustc-std-workspace-alloc\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.5.0\"},{\"features\":[\"Win32_Foundation\",\"Win32_Networking_WinSock\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{\"all-apis\":[\"event\",\"fs\",\"io_uring\",\"mm\",\"mount\",\"net\",\"param\",\"pipe\",\"process\",\"pty\",\"rand\",\"runtime\",\"shm\",\"stdio\",\"system\",\"termios\",\"thread\",\"time\"],\"alloc\":[],\"default\":[\"std\"],\"event\":[],\"fs\":[],\"io_uring\":[\"event\",\"fs\",\"net\",\"thread\",\"linux-raw-sys/io_uring\"],\"linux_4_11\":[],\"linux_5_1\":[\"linux_4_11\"],\"linux_5_11\":[\"linux_5_1\"],\"linux_latest\":[\"linux_5_11\"],\"mm\":[],\"mount\":[],\"net\":[\"linux-raw-sys/net\",\"linux-raw-sys/netlink\",\"linux-raw-sys/if_ether\",\"linux-raw-sys/xdp\"],\"param\":[],\"pipe\":[],\"process\":[\"linux-raw-sys/prctl\"],\"pty\":[\"fs\"],\"rand\":[],\"runtime\":[\"linux-raw-sys/prctl\"],\"rustc-dep-of-std\":[\"core\",\"rustc-std-workspace-alloc\",\"linux-raw-sys/rustc-dep-of-std\",\"bitflags/rustc-dep-of-std\"],\"shm\":[\"fs\"],\"std\":[\"bitflags/std\",\"alloc\",\"libc?/std\",\"libc_errno?/std\"],\"stdio\":[],\"system\":[\"linux-raw-sys/system\"],\"termios\":[],\"thread\":[\"linux-raw-sys/prctl\"],\"time\":[],\"try_close\":[],\"use-explicitly-provided-auxv\":[],\"use-libc\":[\"libc_errno\",\"libc\"],\"use-libc-auxv\":[]}}", + "rustls-native-certs_0.8.3": "{\"dependencies\":[{\"name\":\"openssl-probe\",\"req\":\"^0.2\",\"target\":\"cfg(all(unix, not(target_os = \\\"macos\\\")))\"},{\"features\":[\"std\"],\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.10\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17\"},{\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"kind\":\"dev\",\"name\":\"rustls-webpki\",\"req\":\"^0.103\"},{\"name\":\"schannel\",\"req\":\"^0.1\",\"target\":\"cfg(windows)\"},{\"name\":\"security-framework\",\"req\":\"^3\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.5\"},{\"kind\":\"dev\",\"name\":\"untrusted\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"webpki-roots\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.18\"}],\"features\":{}}", + "rustls-pki-types_1.14.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"crabgrind\",\"req\":\"=0.1.9\",\"target\":\"cfg(all(target_os = \\\"linux\\\", target_arch = \\\"x86_64\\\"))\"},{\"name\":\"web-time\",\"optional\":true,\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[\"dep:zeroize\"],\"default\":[\"alloc\"],\"std\":[\"alloc\"],\"web\":[\"web-time\"]}}", + "rustls-webpki_0.103.9": "{\"dependencies\":[{\"default_features\":false,\"name\":\"aws-lc-rs\",\"optional\":true,\"req\":\"^1.14\"},{\"kind\":\"dev\",\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"kind\":\"dev\",\"name\":\"bzip2\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.17.2\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.12\"},{\"default_features\":false,\"features\":[\"aws_lc_rs\"],\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14.2\"},{\"default_features\":false,\"name\":\"ring\",\"optional\":true,\"req\":\"^0.17\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"untrusted\",\"req\":\"^0.9\"}],\"features\":{\"alloc\":[\"ring?/alloc\",\"pki-types/alloc\"],\"aws-lc-rs\":[\"dep:aws-lc-rs\",\"aws-lc-rs/aws-lc-sys\",\"aws-lc-rs/prebuilt-nasm\"],\"aws-lc-rs-fips\":[\"dep:aws-lc-rs\",\"aws-lc-rs/fips\"],\"aws-lc-rs-unstable\":[\"aws-lc-rs\",\"aws-lc-rs/unstable\"],\"default\":[\"std\"],\"ring\":[\"dep:ring\"],\"std\":[\"alloc\",\"pki-types/std\"]}}", + "rustls_0.23.36": "{\"dependencies\":[{\"default_features\":false,\"name\":\"aws-lc-rs\",\"optional\":true,\"req\":\"^1.14\"},{\"kind\":\"dev\",\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"brotli\",\"optional\":true,\"req\":\"^8\"},{\"name\":\"brotli-decompressor\",\"optional\":true,\"req\":\"^5.0.0\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"default_features\":false,\"features\":[\"default-hasher\",\"inline-more\"],\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.8\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.8\"},{\"kind\":\"dev\",\"name\":\"macro_rules_attribute\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"num-bigint\",\"req\":\"^0.4.4\"},{\"default_features\":false,\"features\":[\"alloc\",\"race\"],\"name\":\"once_cell\",\"req\":\"^1.16\"},{\"features\":[\"alloc\"],\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.12\"},{\"default_features\":false,\"features\":[\"pem\",\"aws_lc_rs\"],\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"name\":\"ring\",\"optional\":true,\"req\":\"^0.17\"},{\"kind\":\"build\",\"name\":\"rustversion\",\"optional\":true,\"req\":\"^1.0.6\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.6\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103.5\"},{\"kind\":\"dev\",\"name\":\"webpki-roots\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17\"},{\"name\":\"zeroize\",\"req\":\"^1.8\"},{\"name\":\"zlib-rs\",\"optional\":true,\"req\":\"^0.5\"}],\"features\":{\"aws-lc-rs\":[\"aws_lc_rs\"],\"aws_lc_rs\":[\"dep:aws-lc-rs\",\"webpki/aws-lc-rs\",\"aws-lc-rs/aws-lc-sys\",\"aws-lc-rs/prebuilt-nasm\"],\"brotli\":[\"dep:brotli\",\"dep:brotli-decompressor\",\"std\"],\"custom-provider\":[],\"default\":[\"aws_lc_rs\",\"logging\",\"prefer-post-quantum\",\"std\",\"tls12\"],\"fips\":[\"aws_lc_rs\",\"aws-lc-rs?/fips\",\"webpki/aws-lc-rs-fips\"],\"logging\":[\"log\"],\"prefer-post-quantum\":[\"aws_lc_rs\"],\"read_buf\":[\"rustversion\",\"std\"],\"ring\":[\"dep:ring\",\"webpki/ring\"],\"std\":[\"webpki/std\",\"pki-types/std\",\"once_cell/std\"],\"tls12\":[],\"zlib\":[\"dep:zlib-rs\"]}}", + "rustversion_1.0.22": "{\"dependencies\":[{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.49\"}],\"features\":{}}", "rustyline_14.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.2\"},{\"name\":\"bitflags\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"buffer-redux\",\"optional\":true,\"req\":\"^1.0\",\"target\":\"cfg(unix)\"},{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"clipboard-win\",\"req\":\"^5.0\",\"target\":\"cfg(windows)\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"name\":\"fd-lock\",\"optional\":true,\"req\":\"^4.0.0\"},{\"name\":\"home\",\"optional\":true,\"req\":\"^0.5.4\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"memchr\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"fs\",\"ioctl\",\"poll\",\"signal\",\"term\"],\"name\":\"nix\",\"req\":\"^0.28\",\"target\":\"cfg(unix)\"},{\"name\":\"radix_trie\",\"optional\":true,\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"regex\",\"optional\":true,\"req\":\"^1.5.5\"},{\"default_features\":false,\"features\":[\"bundled\",\"backup\"],\"name\":\"rusqlite\",\"optional\":true,\"req\":\"^0.31.0\"},{\"name\":\"rustyline-derive\",\"optional\":true,\"req\":\"^0.10.0\"},{\"default_features\":false,\"name\":\"signal-hook\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(unix)\"},{\"default_features\":false,\"name\":\"skim\",\"optional\":true,\"req\":\"^0.10\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\"},{\"name\":\"termios\",\"optional\":true,\"req\":\"^0.3.3\",\"target\":\"cfg(unix)\"},{\"name\":\"unicode-segmentation\",\"req\":\"^1.0\"},{\"name\":\"unicode-width\",\"req\":\"^0.1\"},{\"name\":\"utf8parse\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Console\",\"Win32_Security\",\"Win32_System_Threading\",\"Win32_UI_Input_KeyboardAndMouse\"],\"name\":\"windows-sys\",\"req\":\"^0.52.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"case_insensitive_history_search\":[\"regex\"],\"custom-bindings\":[\"radix_trie\"],\"default\":[\"custom-bindings\",\"with-dirs\",\"with-file-history\"],\"derive\":[\"rustyline-derive\"],\"with-dirs\":[\"home\"],\"with-file-history\":[\"fd-lock\"],\"with-fuzzy\":[\"skim\"],\"with-sqlite-history\":[\"rusqlite\"]}}", - "ryu_1.0.20": "{\"dependencies\":[{\"name\":\"no-panic\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.4\"}],\"features\":{\"small\":[]}}", + "ryu_1.0.22": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\",\"target\":\"cfg(not(miri))\"},{\"name\":\"no-panic\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.4\"}],\"features\":{\"small\":[]}}", + "salsa20_0.10.2": "{\"dependencies\":[{\"name\":\"cipher\",\"req\":\"^0.4.2\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"cipher\",\"req\":\"^0.4.2\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3.3\"}],\"features\":{\"std\":[\"cipher/std\"],\"zeroize\":[\"cipher/zeroize\"]}}", "same-file_1.0.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"winapi-util\",\"req\":\"^0.1.1\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "scc_2.4.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"name\":\"equivalent\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.7\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"name\":\"sdd\",\"req\":\"^3.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.47\"}],\"features\":{\"loom\":[\"dep:loom\",\"sdd/loom\"]}}", "schannel_0.1.28": "{\"dependencies\":[{\"features\":[\"Win32_Foundation\",\"Win32_Security_Cryptography\",\"Win32_Security_Authentication_Identity\",\"Win32_Security_Credentials\",\"Win32_System_LibraryLoader\",\"Win32_System_Memory\",\"Win32_System_SystemInformation\"],\"name\":\"windows-sys\",\"req\":\"^0.61\"},{\"features\":[\"Win32_System_SystemInformation\",\"Win32_System_Time\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\"}],\"features\":{}}", @@ -1090,64 +1139,68 @@ "schemafy_lib_0.5.2": "{\"dependencies\":[{\"name\":\"Inflector\",\"req\":\"^0.11\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"name\":\"schemafy_core\",\"req\":\"^0.5.2\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"syn\",\"req\":\"^1.0\"}],\"features\":{}}", "schemars_0.8.22": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arrayvec05\",\"optional\":true,\"package\":\"arrayvec\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"arrayvec07\",\"optional\":true,\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"bigdecimal03\",\"optional\":true,\"package\":\"bigdecimal\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"bigdecimal04\",\"optional\":true,\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"dyn-clone\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"either\",\"optional\":true,\"req\":\"^1.3\"},{\"name\":\"enumset\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"serde-1\"],\"name\":\"indexmap\",\"optional\":true,\"req\":\"^1.2\"},{\"features\":[\"serde\"],\"name\":\"indexmap2\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.2.1\"},{\"default_features\":false,\"name\":\"rust_decimal\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"schemars_derive\",\"optional\":true,\"req\":\"=0.8.22\"},{\"features\":[\"serde\"],\"name\":\"semver\",\"optional\":true,\"req\":\"^1.0.9\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0.25\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"smol_str\",\"optional\":true,\"req\":\"^0.1.17\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"url\",\"optional\":true,\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"uuid08\",\"optional\":true,\"package\":\"uuid\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"uuid1\",\"optional\":true,\"package\":\"uuid\",\"req\":\"^1.0\"}],\"features\":{\"arrayvec\":[\"arrayvec05\"],\"bigdecimal\":[\"bigdecimal03\"],\"default\":[\"derive\"],\"derive\":[\"schemars_derive\"],\"derive_json_schema\":[\"impl_json_schema\"],\"impl_json_schema\":[\"derive\"],\"indexmap1\":[\"indexmap\"],\"preserve_order\":[\"indexmap\"],\"raw_value\":[\"serde_json/raw_value\"],\"ui_test\":[],\"uuid\":[\"uuid08\"]}}", "schemars_0.9.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arrayvec07\",\"optional\":true,\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"arrayvec07\",\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"bigdecimal04\",\"optional\":true,\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bigdecimal04\",\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"bytes1\",\"optional\":true,\"package\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bytes1\",\"package\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"chrono04\",\"optional\":true,\"package\":\"chrono\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono04\",\"package\":\"chrono\",\"req\":\"^0.4\"},{\"name\":\"dyn-clone\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"either1\",\"optional\":true,\"package\":\"either\",\"req\":\"^1.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"either1\",\"package\":\"either\",\"req\":\"^1.3\"},{\"features\":[\"derive\",\"email\",\"regex\",\"url\"],\"kind\":\"dev\",\"name\":\"garde\",\"req\":\"^0.22\"},{\"default_features\":false,\"name\":\"indexmap2\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"indexmap2\",\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"jiff02\",\"optional\":true,\"package\":\"jiff\",\"req\":\"^0.2\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"jiff02\",\"package\":\"jiff\",\"req\":\"^0.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"jsonschema\",\"req\":\"^0.30\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.2.1\"},{\"name\":\"ref-cast\",\"req\":\"^1.0.22\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.10.6\"},{\"default_features\":false,\"name\":\"rust_decimal1\",\"optional\":true,\"package\":\"rust_decimal\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"rust_decimal1\",\"package\":\"rust_decimal\",\"req\":\"^1\"},{\"name\":\"schemars_derive\",\"optional\":true,\"req\":\"=0.9.0\"},{\"default_features\":false,\"name\":\"semver1\",\"optional\":true,\"package\":\"semver\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"semver1\",\"package\":\"semver\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_json\",\"req\":\"^1.0.127\"},{\"kind\":\"dev\",\"name\":\"serde_repr\",\"req\":\"^0.1.19\"},{\"default_features\":false,\"name\":\"smallvec1\",\"optional\":true,\"package\":\"smallvec\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smallvec1\",\"package\":\"smallvec\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"smol_str02\",\"optional\":true,\"package\":\"smol_str\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smol_str02\",\"package\":\"smol_str\",\"req\":\"^0.2.1\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.17\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"url2\",\"optional\":true,\"package\":\"url\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"serde\",\"std\"],\"kind\":\"dev\",\"name\":\"url2\",\"package\":\"url\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"uuid1\",\"optional\":true,\"package\":\"uuid\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"uuid1\",\"package\":\"uuid\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"validator\",\"req\":\"^0.20\"}],\"features\":{\"_ui_test\":[],\"default\":[\"derive\",\"std\"],\"derive\":[\"schemars_derive\"],\"preserve_order\":[\"serde_json/preserve_order\"],\"raw_value\":[\"serde_json/raw_value\"],\"std\":[]}}", - "schemars_1.0.4": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arrayvec07\",\"optional\":true,\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"arrayvec07\",\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"bigdecimal04\",\"optional\":true,\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bigdecimal04\",\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"bytes1\",\"optional\":true,\"package\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bytes1\",\"package\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"chrono04\",\"optional\":true,\"package\":\"chrono\",\"req\":\"^0.4.39\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono04\",\"package\":\"chrono\",\"req\":\"^0.4\"},{\"name\":\"dyn-clone\",\"req\":\"^1.0.17\"},{\"default_features\":false,\"name\":\"either1\",\"optional\":true,\"package\":\"either\",\"req\":\"^1.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"either1\",\"package\":\"either\",\"req\":\"^1.3\"},{\"features\":[\"derive\",\"email\",\"regex\",\"url\"],\"kind\":\"dev\",\"name\":\"garde\",\"req\":\"^0.22\"},{\"default_features\":false,\"name\":\"indexmap2\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^2.2.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"indexmap2\",\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"jiff02\",\"optional\":true,\"package\":\"jiff\",\"req\":\"^0.2\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"jiff02\",\"package\":\"jiff\",\"req\":\"^0.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"jsonschema\",\"req\":\"^0.30\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.2.1\"},{\"name\":\"ref-cast\",\"req\":\"^1.0.22\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.10.6\"},{\"default_features\":false,\"name\":\"rust_decimal1\",\"optional\":true,\"package\":\"rust_decimal\",\"req\":\"^1.13\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"rust_decimal1\",\"package\":\"rust_decimal\",\"req\":\"^1\"},{\"name\":\"schemars_derive\",\"optional\":true,\"req\":\"=1.0.4\"},{\"default_features\":false,\"name\":\"semver1\",\"optional\":true,\"package\":\"semver\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"semver1\",\"package\":\"semver\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"req\":\"^1.0.194\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_json\",\"req\":\"^1.0.127\"},{\"kind\":\"dev\",\"name\":\"serde_repr\",\"req\":\"^0.1.19\"},{\"default_features\":false,\"name\":\"smallvec1\",\"optional\":true,\"package\":\"smallvec\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smallvec1\",\"package\":\"smallvec\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"smol_str02\",\"optional\":true,\"package\":\"smol_str\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smol_str02\",\"package\":\"smol_str\",\"req\":\"^0.2.1\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.17\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"url2\",\"optional\":true,\"package\":\"url\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"serde\",\"std\"],\"kind\":\"dev\",\"name\":\"url2\",\"package\":\"url\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"uuid1\",\"optional\":true,\"package\":\"uuid\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"uuid1\",\"package\":\"uuid\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"validator\",\"req\":\"^0.20\"}],\"features\":{\"_ui_test\":[],\"default\":[\"derive\",\"std\"],\"derive\":[\"schemars_derive\"],\"preserve_order\":[\"serde_json/preserve_order\"],\"raw_value\":[\"serde_json/raw_value\"],\"std\":[]}}", + "schemars_1.2.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arrayvec07\",\"optional\":true,\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"arrayvec07\",\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"name\":\"bigdecimal04\",\"optional\":true,\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bigdecimal04\",\"package\":\"bigdecimal\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"bytes1\",\"optional\":true,\"package\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"bytes1\",\"package\":\"bytes\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"chrono04\",\"optional\":true,\"package\":\"chrono\",\"req\":\"^0.4.39\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono04\",\"package\":\"chrono\",\"req\":\"^0.4\"},{\"name\":\"dyn-clone\",\"req\":\"^1.0.17\"},{\"default_features\":false,\"name\":\"either1\",\"optional\":true,\"package\":\"either\",\"req\":\"^1.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"either1\",\"package\":\"either\",\"req\":\"^1.3\"},{\"features\":[\"derive\",\"email\",\"regex\",\"url\"],\"kind\":\"dev\",\"name\":\"garde\",\"req\":\"^0.22\"},{\"default_features\":false,\"name\":\"indexmap2\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^2.2.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"indexmap2\",\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"jiff02\",\"optional\":true,\"package\":\"jiff\",\"req\":\"^0.2\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"jiff02\",\"package\":\"jiff\",\"req\":\"^0.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"jsonschema\",\"req\":\"^0.30\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.2.1\"},{\"name\":\"ref-cast\",\"req\":\"^1.0.22\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.10.6\"},{\"default_features\":false,\"name\":\"rust_decimal1\",\"optional\":true,\"package\":\"rust_decimal\",\"req\":\"^1.13\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"rust_decimal1\",\"package\":\"rust_decimal\",\"req\":\"^1\"},{\"name\":\"schemars_derive\",\"optional\":true,\"req\":\"=1.2.1\"},{\"default_features\":false,\"name\":\"semver1\",\"optional\":true,\"package\":\"semver\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"semver1\",\"package\":\"semver\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"req\":\"^1.0.194\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_json\",\"req\":\"^1.0.127\"},{\"kind\":\"dev\",\"name\":\"serde_repr\",\"req\":\"^0.1.19\"},{\"default_features\":false,\"name\":\"smallvec1\",\"optional\":true,\"package\":\"smallvec\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smallvec1\",\"package\":\"smallvec\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"smol_str02\",\"optional\":true,\"package\":\"smol_str\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smol_str02\",\"package\":\"smol_str\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"name\":\"smol_str03\",\"optional\":true,\"package\":\"smol_str\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"smol_str03\",\"package\":\"smol_str\",\"req\":\"^0.3.2\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.17\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"url2\",\"optional\":true,\"package\":\"url\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"serde\",\"std\"],\"kind\":\"dev\",\"name\":\"url2\",\"package\":\"url\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"uuid1\",\"optional\":true,\"package\":\"uuid\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"uuid1\",\"package\":\"uuid\",\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"validator\",\"req\":\"^0.20\"}],\"features\":{\"_ui_test\":[],\"default\":[\"derive\",\"std\"],\"derive\":[\"schemars_derive\"],\"preserve_order\":[\"serde_json/preserve_order\"],\"raw_value\":[\"serde_json/raw_value\"],\"std\":[]}}", "schemars_derive_0.8.22": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.2.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"name\":\"serde_derive_internals\",\"req\":\"^0.29\"},{\"features\":[\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", - "schemars_derive_1.0.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.2.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"name\":\"serde_derive_internals\",\"req\":\"^0.29.1\"},{\"name\":\"syn\",\"req\":\"^2.0.46\"},{\"features\":[\"extra-traits\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", + "schemars_derive_1.2.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.2.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"name\":\"serde_derive_internals\",\"req\":\"^0.29.1\"},{\"name\":\"syn\",\"req\":\"^2.0.46\"},{\"features\":[\"extra-traits\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", "scoped-tls_1.0.1": "{\"dependencies\":[],\"features\":{}}", "scopeguard_1.2.0": "{\"dependencies\":[],\"features\":{\"default\":[\"use_std\"],\"use_std\":[]}}", + "scrypt_0.11.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"rand_core\"],\"name\":\"password-hash\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"rand_core\"],\"kind\":\"dev\",\"name\":\"password-hash\",\"req\":\"^0.5\"},{\"name\":\"pbkdf2\",\"req\":\"^0.12\"},{\"default_features\":false,\"name\":\"salsa20\",\"req\":\"^0.10.2\"},{\"default_features\":false,\"name\":\"sha2\",\"req\":\"^0.10\"}],\"features\":{\"default\":[\"simple\",\"std\"],\"simple\":[\"password-hash\"],\"std\":[\"password-hash/std\"]}}", "sdd_3.0.10": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.6\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"}],\"features\":{}}", "seccompiler_0.5.0": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.153\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.27\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.9\"}],\"features\":{\"json\":[\"serde\",\"serde_json\"]}}", + "secrecy_0.10.3": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"zeroize\",\"req\":\"^1.6\"}],\"features\":{}}", "secret-service_4.0.0": "{\"dependencies\":[{\"name\":\"aes\",\"optional\":true,\"req\":\"^0.8\"},{\"features\":[\"block-padding\",\"alloc\"],\"name\":\"cbc\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"generic-array\",\"req\":\"^0.14\"},{\"name\":\"hkdf\",\"optional\":true,\"req\":\"^0.12.0\"},{\"name\":\"num\",\"req\":\"^0.4.0\"},{\"name\":\"once_cell\",\"req\":\"^1\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10.40\"},{\"name\":\"rand\",\"req\":\"^0.8.1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.103\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"test-with\",\"req\":\"^0.8\"},{\"features\":[\"rt\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"zbus\",\"req\":\"^4\"}],\"features\":{\"crypto-openssl\":[\"dep:openssl\"],\"crypto-rust\":[\"dep:aes\",\"dep:cbc\",\"dep:sha2\",\"dep:hkdf\"],\"rt-async-io-crypto-openssl\":[\"zbus/async-io\",\"crypto-openssl\"],\"rt-async-io-crypto-rust\":[\"zbus/async-io\",\"crypto-rust\"],\"rt-tokio-crypto-openssl\":[\"zbus/tokio\",\"crypto-openssl\"],\"rt-tokio-crypto-rust\":[\"zbus/tokio\",\"crypto-rust\"]}}", "security-framework-sys_2.15.0": "{\"dependencies\":[{\"name\":\"core-foundation-sys\",\"req\":\"^0.8.6\"},{\"name\":\"libc\",\"req\":\"^0.2.150\"}],\"features\":{\"OSX_10_10\":[\"OSX_10_9\"],\"OSX_10_11\":[\"OSX_10_10\"],\"OSX_10_12\":[\"OSX_10_11\"],\"OSX_10_13\":[\"OSX_10_12\"],\"OSX_10_14\":[\"OSX_10_13\"],\"OSX_10_15\":[\"OSX_10_14\"],\"OSX_10_9\":[],\"default\":[\"OSX_10_12\"]}}", "security-framework_2.11.1": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.6\"},{\"name\":\"core-foundation\",\"req\":\"^0.9.4\"},{\"name\":\"core-foundation-sys\",\"req\":\"^0.8.6\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"libc\",\"req\":\"^0.2.139\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.20\"},{\"name\":\"num-bigint\",\"optional\":true,\"req\":\"^0.4.6\"},{\"default_features\":false,\"name\":\"security-framework-sys\",\"req\":\"^2.11.1\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.3.0\"},{\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.17\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.16\"}],\"features\":{\"OSX_10_10\":[\"OSX_10_9\",\"security-framework-sys/OSX_10_10\"],\"OSX_10_11\":[\"OSX_10_10\",\"security-framework-sys/OSX_10_11\"],\"OSX_10_12\":[\"OSX_10_11\",\"security-framework-sys/OSX_10_12\"],\"OSX_10_13\":[\"OSX_10_12\",\"security-framework-sys/OSX_10_13\",\"alpn\",\"session-tickets\",\"serial-number-bigint\"],\"OSX_10_14\":[\"OSX_10_13\",\"security-framework-sys/OSX_10_14\"],\"OSX_10_15\":[\"OSX_10_14\",\"security-framework-sys/OSX_10_15\"],\"OSX_10_9\":[\"security-framework-sys/OSX_10_9\"],\"alpn\":[],\"default\":[\"OSX_10_12\"],\"job-bless\":[],\"nightly\":[],\"serial-number-bigint\":[\"dep:num-bigint\"],\"session-tickets\":[]}}", "security-framework_3.5.1": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.6\"},{\"name\":\"core-foundation\",\"req\":\"^0.10\"},{\"name\":\"core-foundation-sys\",\"req\":\"^0.8.6\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"libc\",\"req\":\"^0.2.139\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.20\"},{\"default_features\":false,\"name\":\"security-framework-sys\",\"req\":\"^2.15\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.12.0\"},{\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.23\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.16\"}],\"features\":{\"OSX_10_12\":[\"security-framework-sys/OSX_10_12\"],\"OSX_10_13\":[\"OSX_10_12\",\"security-framework-sys/OSX_10_13\",\"alpn\",\"session-tickets\"],\"OSX_10_14\":[\"OSX_10_13\",\"security-framework-sys/OSX_10_14\"],\"OSX_10_15\":[\"OSX_10_14\",\"security-framework-sys/OSX_10_15\"],\"alpn\":[],\"default\":[\"OSX_10_12\"],\"job-bless\":[],\"nightly\":[],\"session-tickets\":[],\"sync-keychain\":[\"OSX_10_13\"]}}", + "self_cell_0.10.3": "{\"dependencies\":[{\"name\":\"new_self_cell\",\"package\":\"self_cell\",\"req\":\"^1\"}],\"features\":{\"old_rust\":[\"new_self_cell/old_rust\"]}}", + "self_cell_1.2.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"=1.1.0\"},{\"name\":\"rustversion\",\"optional\":true,\"req\":\">=1\"}],\"features\":{\"old_rust\":[\"rustversion\"]}}", "semver_1.0.27": "{\"dependencies\":[{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"package\":\"serde_core\",\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"}],\"features\":{\"default\":[\"std\"],\"serde\":[\"dep:serde\"],\"std\":[]}}", - "sentry-actix_0.46.0": "{\"dependencies\":[{\"name\":\"actix-http\",\"req\":\"^3.10\"},{\"default_features\":false,\"name\":\"actix-web\",\"req\":\"^4\"},{\"kind\":\"dev\",\"name\":\"actix-web\",\"req\":\"^4\"},{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-util\",\"req\":\"^0.3.5\"},{\"default_features\":false,\"features\":[\"client\"],\"name\":\"sentry-core\",\"req\":\"^0.46.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.44\"}],\"features\":{\"default\":[\"release-health\"],\"release-health\":[\"sentry-core/release-health\"]}}", - "sentry-backtrace_0.46.0": "{\"dependencies\":[{\"name\":\"backtrace\",\"req\":\"^0.3.44\"},{\"default_features\":false,\"features\":[\"std\",\"unicode-perl\"],\"name\":\"regex\",\"req\":\"^1.5.5\"},{\"name\":\"sentry-core\",\"req\":\"^0.46.0\"}],\"features\":{}}", - "sentry-contexts_0.46.0": "{\"dependencies\":[{\"name\":\"hostname\",\"req\":\"^0.4\"},{\"name\":\"libc\",\"req\":\"^0.2.66\"},{\"name\":\"os_info\",\"req\":\"^3.5.0\",\"target\":\"cfg(windows)\"},{\"kind\":\"build\",\"name\":\"rustc_version\",\"req\":\"^0.4.0\"},{\"name\":\"sentry-core\",\"req\":\"^0.46.0\"},{\"name\":\"uname\",\"req\":\"^0.1.1\",\"target\":\"cfg(not(windows))\"}],\"features\":{}}", - "sentry-core_0.46.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.30\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.24\"},{\"features\":[\"std\"],\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.8\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.5.3\"},{\"name\":\"sentry-types\",\"req\":\"^0.46.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.104\"},{\"name\":\"serde_json\",\"req\":\"^1.0.46\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^2.0.12\"},{\"features\":[\"rt\",\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.44\"},{\"name\":\"url\",\"req\":\"^2.1.1\"},{\"features\":[\"v4\",\"serde\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.0.0\"}],\"features\":{\"client\":[\"rand\"],\"default\":[],\"logs\":[],\"release-health\":[],\"test\":[\"client\",\"release-health\"]}}", - "sentry-debug-images_0.46.0": "{\"dependencies\":[{\"name\":\"findshlibs\",\"req\":\"=0.10.2\"},{\"name\":\"sentry-core\",\"req\":\"^0.46.0\"}],\"features\":{}}", - "sentry-panic_0.46.0": "{\"dependencies\":[{\"name\":\"sentry-backtrace\",\"req\":\"^0.46.0\"},{\"features\":[\"client\"],\"name\":\"sentry-core\",\"req\":\"^0.46.0\"}],\"features\":{}}", - "sentry-tracing_0.46.0": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.9.4\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"sentry-backtrace\",\"optional\":true,\"req\":\"^0.46.0\"},{\"features\":[\"client\"],\"name\":\"sentry-core\",\"req\":\"^0.46.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"features\":[\"rt-multi-thread\",\"macros\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.44\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1\"},{\"name\":\"tracing-core\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3.20\"},{\"features\":[\"fmt\",\"registry\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.20\"}],\"features\":{\"backtrace\":[\"dep:sentry-backtrace\"],\"default\":[],\"logs\":[\"sentry-core/logs\"]}}", - "sentry-types_0.46.0": "{\"dependencies\":[{\"features\":[\"serde\"],\"name\":\"debugid\",\"req\":\"^0.8.0\"},{\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"rand\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.25.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.104\"},{\"name\":\"serde_json\",\"req\":\"^1.0.46\"},{\"name\":\"thiserror\",\"req\":\"^2.0.12\"},{\"features\":[\"formatting\",\"parsing\"],\"name\":\"time\",\"req\":\"^0.3.5\"},{\"features\":[\"serde\"],\"name\":\"url\",\"req\":\"^2.1.1\"},{\"features\":[\"serde\"],\"name\":\"uuid\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[\"protocol\"],\"protocol\":[]}}", - "sentry_0.46.0": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"actix-web\",\"req\":\"^4\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.30\"},{\"name\":\"curl\",\"optional\":true,\"req\":\"^0.4.25\"},{\"name\":\"embedded-svc\",\"optional\":true,\"req\":\"^0.28.1\"},{\"name\":\"esp-idf-svc\",\"optional\":true,\"req\":\"^0.51.0\",\"target\":\"cfg(target_os = \\\"espidf\\\")\"},{\"name\":\"httpdate\",\"optional\":true,\"req\":\"^1.0.0\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.8\"},{\"name\":\"native-tls\",\"optional\":true,\"req\":\"^0.2.8\"},{\"kind\":\"dev\",\"name\":\"pretty_env_logger\",\"req\":\"^0.5.0\"},{\"default_features\":false,\"features\":[\"blocking\",\"json\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12\"},{\"default_features\":false,\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.18\"},{\"default_features\":false,\"name\":\"sentry-actix\",\"optional\":true,\"req\":\"^0.46.0\"},{\"name\":\"sentry-anyhow\",\"optional\":true,\"req\":\"^0.46.0\"},{\"name\":\"sentry-backtrace\",\"optional\":true,\"req\":\"^0.46.0\"},{\"name\":\"sentry-contexts\",\"optional\":true,\"req\":\"^0.46.0\"},{\"features\":[\"client\"],\"name\":\"sentry-core\",\"req\":\"^0.46.0\"},{\"name\":\"sentry-debug-images\",\"optional\":true,\"req\":\"^0.46.0\"},{\"name\":\"sentry-log\",\"optional\":true,\"req\":\"^0.46.0\"},{\"name\":\"sentry-opentelemetry\",\"optional\":true,\"req\":\"^0.46.0\"},{\"name\":\"sentry-panic\",\"optional\":true,\"req\":\"^0.46.0\"},{\"name\":\"sentry-slog\",\"optional\":true,\"req\":\"^0.46.0\"},{\"name\":\"sentry-tower\",\"optional\":true,\"req\":\"^0.46.0\"},{\"name\":\"sentry-tracing\",\"optional\":true,\"req\":\"^0.46.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.48\"},{\"kind\":\"dev\",\"name\":\"slog\",\"req\":\"^2.5.2\"},{\"features\":[\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.44\"},{\"features\":[\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.44\"},{\"features\":[\"util\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"fmt\",\"tracing-log\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"ureq\",\"optional\":true,\"req\":\"^3.0.11\"}],\"features\":{\"actix\":[\"sentry-actix\"],\"anyhow\":[\"sentry-anyhow\"],\"backtrace\":[\"sentry-backtrace\",\"sentry-tracing?/backtrace\"],\"contexts\":[\"sentry-contexts\"],\"curl\":[\"dep:curl\",\"httpdate\"],\"debug-images\":[\"sentry-debug-images\"],\"default\":[\"backtrace\",\"contexts\",\"debug-images\",\"panic\",\"transport\",\"release-health\"],\"embedded-svc-http\":[\"dep:embedded-svc\",\"dep:esp-idf-svc\"],\"log\":[\"sentry-log\"],\"logs\":[\"sentry-core/logs\",\"sentry-tracing?/logs\",\"sentry-log?/logs\"],\"native-tls\":[\"dep:native-tls\",\"reqwest?/default-tls\",\"ureq?/native-tls\"],\"opentelemetry\":[\"sentry-opentelemetry\"],\"panic\":[\"sentry-panic\"],\"release-health\":[\"sentry-core/release-health\",\"sentry-actix?/release-health\"],\"reqwest\":[\"dep:reqwest\",\"httpdate\",\"tokio\"],\"rustls\":[\"dep:rustls\",\"reqwest?/rustls-tls\",\"ureq?/rustls\"],\"slog\":[\"sentry-slog\"],\"test\":[\"sentry-core/test\"],\"tower\":[\"sentry-tower\"],\"tower-axum-matched-path\":[\"tower-http\",\"sentry-tower/axum-matched-path\"],\"tower-http\":[\"tower\",\"sentry-tower/http\"],\"tracing\":[\"sentry-tracing\"],\"transport\":[\"reqwest\",\"native-tls\"],\"ureq\":[\"dep:ureq\",\"httpdate\"]}}", + "sentry-actix_0.46.1": "{\"dependencies\":[{\"name\":\"actix-http\",\"req\":\"^3.10\"},{\"default_features\":false,\"name\":\"actix-web\",\"req\":\"^4\"},{\"kind\":\"dev\",\"name\":\"actix-web\",\"req\":\"^4\"},{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-util\",\"req\":\"^0.3.5\"},{\"default_features\":false,\"features\":[\"client\"],\"name\":\"sentry-core\",\"req\":\"^0.46.1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.44\"}],\"features\":{\"default\":[\"release-health\"],\"release-health\":[\"sentry-core/release-health\"]}}", + "sentry-backtrace_0.46.1": "{\"dependencies\":[{\"name\":\"backtrace\",\"req\":\"^0.3.44\"},{\"default_features\":false,\"features\":[\"std\",\"unicode-perl\"],\"name\":\"regex\",\"req\":\"^1.5.5\"},{\"name\":\"sentry-core\",\"req\":\"^0.46.1\"}],\"features\":{}}", + "sentry-contexts_0.46.1": "{\"dependencies\":[{\"name\":\"hostname\",\"req\":\"^0.4\"},{\"name\":\"libc\",\"req\":\"^0.2.66\"},{\"name\":\"os_info\",\"req\":\"^3.5.0\",\"target\":\"cfg(windows)\"},{\"kind\":\"build\",\"name\":\"rustc_version\",\"req\":\"^0.4.0\"},{\"name\":\"sentry-core\",\"req\":\"^0.46.1\"},{\"name\":\"uname\",\"req\":\"^0.1.1\",\"target\":\"cfg(not(windows))\"}],\"features\":{}}", + "sentry-core_0.46.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.30\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.24\"},{\"features\":[\"std\"],\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.8\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.5.3\"},{\"name\":\"sentry-types\",\"req\":\"^0.46.1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.104\"},{\"name\":\"serde_json\",\"req\":\"^1.0.46\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^2.0.12\"},{\"features\":[\"rt\",\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.44\"},{\"name\":\"url\",\"req\":\"^2.1.1\"},{\"features\":[\"v4\",\"serde\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.0.0\"}],\"features\":{\"client\":[\"rand\"],\"default\":[],\"logs\":[],\"release-health\":[],\"test\":[\"client\",\"release-health\"]}}", + "sentry-debug-images_0.46.1": "{\"dependencies\":[{\"name\":\"findshlibs\",\"req\":\"=0.10.2\"},{\"name\":\"sentry-core\",\"req\":\"^0.46.1\"}],\"features\":{}}", + "sentry-panic_0.46.1": "{\"dependencies\":[{\"name\":\"sentry-backtrace\",\"req\":\"^0.46.1\"},{\"features\":[\"client\"],\"name\":\"sentry-core\",\"req\":\"^0.46.1\"}],\"features\":{}}", + "sentry-tracing_0.46.1": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.9.4\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"sentry-backtrace\",\"optional\":true,\"req\":\"^0.46.1\"},{\"features\":[\"client\"],\"name\":\"sentry-core\",\"req\":\"^0.46.1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"features\":[\"rt-multi-thread\",\"macros\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.44\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1\"},{\"name\":\"tracing-core\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3.20\"},{\"features\":[\"fmt\",\"registry\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.20\"}],\"features\":{\"backtrace\":[\"dep:sentry-backtrace\"],\"default\":[],\"logs\":[\"sentry-core/logs\"]}}", + "sentry-types_0.46.1": "{\"dependencies\":[{\"features\":[\"serde\"],\"name\":\"debugid\",\"req\":\"^0.8.0\"},{\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"rand\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.25.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.104\"},{\"name\":\"serde_json\",\"req\":\"^1.0.46\"},{\"name\":\"thiserror\",\"req\":\"^2.0.12\"},{\"features\":[\"formatting\",\"parsing\"],\"name\":\"time\",\"req\":\"^0.3.5\"},{\"features\":[\"serde\"],\"name\":\"url\",\"req\":\"^2.1.1\"},{\"features\":[\"serde\"],\"name\":\"uuid\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[\"protocol\"],\"protocol\":[]}}", + "sentry_0.46.1": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"actix-web\",\"req\":\"^4\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.30\"},{\"name\":\"curl\",\"optional\":true,\"req\":\"^0.4.25\"},{\"name\":\"embedded-svc\",\"optional\":true,\"req\":\"^0.28.1\"},{\"name\":\"esp-idf-svc\",\"optional\":true,\"req\":\"^0.51.0\",\"target\":\"cfg(target_os = \\\"espidf\\\")\"},{\"name\":\"httpdate\",\"optional\":true,\"req\":\"^1.0.0\"},{\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.8\"},{\"name\":\"native-tls\",\"optional\":true,\"req\":\"^0.2.8\"},{\"kind\":\"dev\",\"name\":\"pretty_env_logger\",\"req\":\"^0.5.0\"},{\"default_features\":false,\"features\":[\"blocking\",\"json\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.12.25\"},{\"default_features\":false,\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.18\"},{\"default_features\":false,\"name\":\"sentry-actix\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-anyhow\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-backtrace\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-contexts\",\"optional\":true,\"req\":\"^0.46.1\"},{\"features\":[\"client\"],\"name\":\"sentry-core\",\"req\":\"^0.46.1\"},{\"name\":\"sentry-debug-images\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-log\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-opentelemetry\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-panic\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-slog\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-tower\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"sentry-tracing\",\"optional\":true,\"req\":\"^0.46.1\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.48\"},{\"kind\":\"dev\",\"name\":\"slog\",\"req\":\"^2.5.2\"},{\"features\":[\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.44\"},{\"features\":[\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.44\"},{\"features\":[\"util\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5.2\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"fmt\",\"tracing-log\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"ureq\",\"optional\":true,\"req\":\"^3.0.11\"}],\"features\":{\"actix\":[\"sentry-actix\"],\"anyhow\":[\"sentry-anyhow\"],\"backtrace\":[\"sentry-backtrace\",\"sentry-tracing?/backtrace\"],\"contexts\":[\"sentry-contexts\"],\"curl\":[\"dep:curl\",\"httpdate\"],\"debug-images\":[\"sentry-debug-images\"],\"default\":[\"backtrace\",\"contexts\",\"debug-images\",\"panic\",\"transport\",\"release-health\"],\"embedded-svc-http\":[\"dep:embedded-svc\",\"dep:esp-idf-svc\"],\"log\":[\"sentry-log\"],\"logs\":[\"sentry-core/logs\",\"sentry-tracing?/logs\",\"sentry-log?/logs\"],\"native-tls\":[\"dep:native-tls\",\"reqwest?/default-tls\",\"ureq?/native-tls\"],\"opentelemetry\":[\"sentry-opentelemetry\"],\"panic\":[\"sentry-panic\"],\"release-health\":[\"sentry-core/release-health\",\"sentry-actix?/release-health\"],\"reqwest\":[\"dep:reqwest\",\"httpdate\",\"tokio\"],\"rustls\":[\"dep:rustls\",\"reqwest?/rustls-tls\",\"ureq?/rustls\"],\"slog\":[\"sentry-slog\"],\"test\":[\"sentry-core/test\"],\"tower\":[\"sentry-tower\"],\"tower-axum-matched-path\":[\"tower-http\",\"sentry-tower/axum-matched-path\"],\"tower-http\":[\"tower\",\"sentry-tower/http\"],\"tracing\":[\"sentry-tracing\"],\"transport\":[\"reqwest\",\"native-tls\"],\"ureq\":[\"dep:ureq\",\"httpdate\"]}}", "serde_1.0.228": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"result\"],\"name\":\"serde_core\",\"req\":\"=1.0.228\"},{\"name\":\"serde_derive\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[\"serde_core/alloc\"],\"default\":[\"std\"],\"derive\":[\"serde_derive\"],\"rc\":[\"serde_core/rc\"],\"std\":[\"serde_core/std\"],\"unstable\":[\"serde_core/unstable\"]}}", "serde_core_1.0.228": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"name\":\"serde_derive\",\"req\":\"=1.0.228\",\"target\":\"cfg(any())\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"}],\"features\":{\"alloc\":[],\"default\":[\"std\",\"result\"],\"rc\":[],\"result\":[],\"std\":[],\"unstable\":[]}}", "serde_derive_1.0.228": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"proc-macro\"],\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"default_features\":false,\"features\":[\"proc-macro\"],\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"clone-impls\",\"derive\",\"parsing\",\"printing\",\"proc-macro\"],\"name\":\"syn\",\"req\":\"^2.0.81\"}],\"features\":{\"default\":[],\"deserialize_in_place\":[]}}", "serde_derive_internals_0.29.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"default_features\":false,\"features\":[\"clone-impls\",\"derive\",\"parsing\",\"printing\"],\"name\":\"syn\",\"req\":\"^2.0.46\"}],\"features\":{}}", "serde_html_form_0.3.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert_matches2\",\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"divan\",\"req\":\"^0.1.11\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"form_urlencoded\",\"req\":\"^1.0.1\"},{\"default_features\":false,\"name\":\"indexmap\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.45.0\"},{\"name\":\"itoa\",\"req\":\"^1.0.1\"},{\"name\":\"ryu\",\"optional\":true,\"req\":\"^1.0.9\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.221\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_core\",\"req\":\"^1.0.221\"},{\"kind\":\"dev\",\"name\":\"serde_urlencoded\",\"req\":\"^0.7.1\"}],\"features\":{\"default\":[\"ryu\",\"std\"],\"std\":[]}}", - "serde_json_1.0.145": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.11\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.2.3\"},{\"kind\":\"dev\",\"name\":\"indoc\",\"req\":\"^2.0.2\"},{\"name\":\"itoa\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"name\":\"ryu\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde\",\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.194\"},{\"kind\":\"dev\",\"name\":\"serde_bytes\",\"req\":\"^0.11.10\"},{\"default_features\":false,\"name\":\"serde_core\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.166\"},{\"kind\":\"dev\",\"name\":\"serde_stacker\",\"req\":\"^0.1.8\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{\"alloc\":[\"serde_core/alloc\"],\"arbitrary_precision\":[],\"default\":[\"std\"],\"float_roundtrip\":[],\"preserve_order\":[\"indexmap\",\"std\"],\"raw_value\":[],\"std\":[\"memchr/std\",\"serde_core/std\"],\"unbounded_depth\":[]}}", + "serde_json_1.0.149": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.11\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.2.3\"},{\"kind\":\"dev\",\"name\":\"indoc\",\"req\":\"^2.0.2\"},{\"name\":\"itoa\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"default_features\":false,\"name\":\"serde\",\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.194\"},{\"kind\":\"dev\",\"name\":\"serde_bytes\",\"req\":\"^0.11.10\"},{\"default_features\":false,\"name\":\"serde_core\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.166\"},{\"kind\":\"dev\",\"name\":\"serde_stacker\",\"req\":\"^0.1.8\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"},{\"name\":\"zmij\",\"req\":\"^1.0\"}],\"features\":{\"alloc\":[\"serde_core/alloc\"],\"arbitrary_precision\":[],\"default\":[\"std\"],\"float_roundtrip\":[],\"preserve_order\":[\"indexmap\",\"std\"],\"raw_value\":[],\"std\":[\"memchr/std\",\"serde_core/std\"],\"unbounded_depth\":[]}}", "serde_path_to_error_0.1.20": "{\"dependencies\":[{\"name\":\"itoa\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde\",\"req\":\"^1.0.220\",\"target\":\"cfg(any())\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.220\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_core\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.100\"}],\"features\":{}}", "serde_repr_0.1.20": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.166\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.100\"},{\"name\":\"syn\",\"req\":\"^2.0.46\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.81\"}],\"features\":{}}", - "serde_spanned_1.0.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.145\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde-untagged\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"}],\"features\":{\"alloc\":[\"serde?/alloc\"],\"default\":[\"std\",\"serde\"],\"serde\":[\"dep:serde\"],\"std\":[\"alloc\",\"serde?/std\"]}}", + "serde_spanned_1.0.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde-untagged\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.225\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"}],\"features\":{\"alloc\":[\"serde_core?/alloc\"],\"default\":[\"std\",\"serde\"],\"serde\":[\"dep:serde_core\"],\"std\":[\"alloc\",\"serde_core?/std\"]}}", "serde_urlencoded_0.7.1": "{\"dependencies\":[{\"name\":\"form_urlencoded\",\"req\":\"^1\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"name\":\"ryu\",\"req\":\"^1\"},{\"name\":\"serde\",\"req\":\"^1.0.69\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"}],\"features\":{}}", "serde_with_3.16.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22.1\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"chrono_0_4\",\"optional\":true,\"package\":\"chrono\",\"req\":\"^0.4.20\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"expect-test\",\"req\":\"^1.5.1\"},{\"kind\":\"dev\",\"name\":\"fnv\",\"req\":\"^1.0.6\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3.3\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"hashbrown_0_14\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.14.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"hashbrown_0_15\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.15.0\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"hashbrown_0_16\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.16.0\"},{\"default_features\":false,\"name\":\"hex\",\"optional\":true,\"req\":\"^0.4.3\"},{\"default_features\":false,\"features\":[\"serde-1\"],\"name\":\"indexmap_1\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^1.8\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"indexmap_2\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"resolve-file\"],\"kind\":\"dev\",\"name\":\"jsonschema\",\"req\":\"^0.33.0\"},{\"kind\":\"dev\",\"name\":\"mime\",\"req\":\"^0.3.16\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4.0\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.12.1\"},{\"kind\":\"dev\",\"name\":\"rmp-serde\",\"req\":\"^1.3.0\"},{\"kind\":\"dev\",\"name\":\"ron\",\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.22\"},{\"default_features\":false,\"name\":\"schemars_0_8\",\"optional\":true,\"package\":\"schemars\",\"req\":\"^0.8.16\"},{\"kind\":\"dev\",\"name\":\"schemars_0_8\",\"package\":\"schemars\",\"req\":\"^0.8.16\"},{\"default_features\":false,\"name\":\"schemars_0_9\",\"optional\":true,\"package\":\"schemars\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"schemars_0_9\",\"package\":\"schemars\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"name\":\"schemars_1\",\"optional\":true,\"package\":\"schemars\",\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"schemars_1\",\"package\":\"schemars\",\"req\":\"^1.0.2\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.152\"},{\"kind\":\"dev\",\"name\":\"serde-xml-rs\",\"req\":\"^0.8.1\"},{\"default_features\":false,\"features\":[\"result\"],\"name\":\"serde_core\",\"req\":\"^1.0.225\"},{\"default_features\":false,\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.145\"},{\"features\":[\"preserve_order\"],\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.25\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.124\"},{\"name\":\"serde_with_macros\",\"optional\":true,\"req\":\"=3.16.1\"},{\"kind\":\"dev\",\"name\":\"serde_yaml\",\"req\":\"^0.9.2\"},{\"default_features\":false,\"name\":\"smallvec_1\",\"optional\":true,\"package\":\"smallvec\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"time_0_3\",\"optional\":true,\"package\":\"time\",\"req\":\"~0.3.36\"}],\"features\":{\"alloc\":[\"serde_core/alloc\",\"base64?/alloc\",\"chrono_0_4?/alloc\",\"hex?/alloc\",\"serde_json?/alloc\",\"time_0_3?/alloc\"],\"base64\":[\"dep:base64\",\"alloc\"],\"chrono\":[\"chrono_0_4\"],\"chrono_0_4\":[\"dep:chrono_0_4\"],\"default\":[\"std\",\"macros\"],\"guide\":[\"dep:document-features\",\"macros\",\"std\"],\"hashbrown_0_14\":[\"dep:hashbrown_0_14\",\"alloc\"],\"hashbrown_0_15\":[\"dep:hashbrown_0_15\",\"alloc\"],\"hashbrown_0_16\":[\"dep:hashbrown_0_16\",\"alloc\"],\"hex\":[\"dep:hex\",\"alloc\"],\"indexmap\":[\"indexmap_1\"],\"indexmap_1\":[\"dep:indexmap_1\",\"alloc\"],\"indexmap_2\":[\"dep:indexmap_2\",\"alloc\"],\"json\":[\"dep:serde_json\",\"alloc\"],\"macros\":[\"dep:serde_with_macros\"],\"schemars_0_8\":[\"dep:schemars_0_8\",\"std\",\"serde_with_macros?/schemars_0_8\"],\"schemars_0_9\":[\"dep:schemars_0_9\",\"alloc\",\"serde_with_macros?/schemars_0_9\",\"dep:serde_json\"],\"schemars_1\":[\"dep:schemars_1\",\"alloc\",\"serde_with_macros?/schemars_1\",\"dep:serde_json\"],\"smallvec_1\":[\"dep:smallvec_1\"],\"std\":[\"alloc\",\"serde_core/std\",\"chrono_0_4?/clock\",\"chrono_0_4?/std\",\"indexmap_1?/std\",\"indexmap_2?/std\",\"time_0_3?/serde-well-known\",\"time_0_3?/std\",\"schemars_0_9?/std\",\"schemars_1?/std\"],\"time_0_3\":[\"dep:time_0_3\"]}}", "serde_with_macros_3.16.1": "{\"dependencies\":[{\"name\":\"darling\",\"req\":\"^0.21.0\"},{\"kind\":\"dev\",\"name\":\"expect-test\",\"req\":\"^1.5.1\"},{\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3.3\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.4.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.1\"},{\"name\":\"quote\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.12.1\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.22\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.152\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.25\"},{\"features\":[\"extra-traits\",\"full\",\"parsing\"],\"name\":\"syn\",\"req\":\"^2.0.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.111\"}],\"features\":{\"schemars_0_8\":[],\"schemars_0_9\":[],\"schemars_1\":[]}}", "serde_yaml_0.9.34+deprecated": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.79\"},{\"name\":\"indexmap\",\"req\":\"^2.2.1\"},{\"kind\":\"dev\",\"name\":\"indoc\",\"req\":\"^2.0\"},{\"name\":\"itoa\",\"req\":\"^1.0\"},{\"name\":\"ryu\",\"req\":\"^1.0\"},{\"name\":\"serde\",\"req\":\"^1.0.195\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.195\"},{\"name\":\"unsafe-libyaml\",\"req\":\"^0.2.11\"}],\"features\":{}}", - "serial2_0.2.31": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert2\",\"req\":\"^0.3.11\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\",\"target\":\"cfg(unix)\"},{\"name\":\"libc\",\"req\":\"^0.2.109\",\"target\":\"cfg(unix)\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.108\"},{\"features\":[\"commapi\",\"fileapi\",\"handleapi\",\"ioapiset\",\"std\",\"synchapi\",\"winbase\",\"winerror\",\"winreg\"],\"name\":\"winapi\",\"req\":\"^0.3.9\",\"target\":\"cfg(windows)\"}],\"features\":{\"doc\":[],\"doc-cfg\":[],\"rs4xx\":[],\"serde\":[\"dep:serde\"],\"unix\":[],\"windows\":[]}}", - "serial_test_3.2.0": "{\"dependencies\":[{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"env_logger\",\"optional\":true,\"req\":\">=0.6.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"fslock\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"executor\"],\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"use_std\"],\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\">=0.4\"},{\"name\":\"log\",\"optional\":true,\"req\":\">=0.4.4\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"once_cell\",\"req\":\"^1.19\"},{\"default_features\":false,\"name\":\"parking_lot\",\"req\":\"^0.12\"},{\"default_features\":false,\"name\":\"scc\",\"req\":\"^2\"},{\"name\":\"serial_test_derive\",\"req\":\"~3.2.0\"}],\"features\":{\"async\":[\"dep:futures\",\"serial_test_derive/async\"],\"default\":[\"logging\",\"async\"],\"docsrs\":[\"dep:document-features\"],\"file_locks\":[\"dep:fslock\"],\"logging\":[\"dep:log\"],\"test_logging\":[\"logging\",\"dep:env_logger\",\"serial_test_derive/test_logging\"]}}", - "serial_test_derive_3.2.0": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\">=0.6.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"prettyplease\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"proc-macro\"],\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"full\",\"printing\",\"parsing\",\"clone-impls\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"async\":[],\"default\":[],\"test_logging\":[]}}", + "serial2_0.2.33": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert2\",\"req\":\"^0.3.11\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\",\"target\":\"cfg(unix)\"},{\"name\":\"libc\",\"req\":\"^0.2.109\",\"target\":\"cfg(unix)\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.108\"},{\"features\":[\"commapi\",\"fileapi\",\"handleapi\",\"ioapiset\",\"std\",\"synchapi\",\"winbase\",\"winerror\",\"winreg\"],\"name\":\"winapi\",\"req\":\"^0.3.9\",\"target\":\"cfg(windows)\"}],\"features\":{\"doc\":[],\"doc-cfg\":[],\"rs4xx\":[],\"serde\":[\"dep:serde\"],\"unix\":[],\"windows\":[]}}", + "serial_test_3.3.1": "{\"dependencies\":[{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"env_logger\",\"optional\":true,\"req\":\">=0.6.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"fslock\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-executor\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"use_std\"],\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\">=0.4\"},{\"name\":\"log\",\"optional\":true,\"req\":\">=0.4.4\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"once_cell\",\"req\":\"^1.19\"},{\"default_features\":false,\"name\":\"parking_lot\",\"req\":\"^0.12\"},{\"default_features\":false,\"name\":\"scc\",\"req\":\"^2\"},{\"name\":\"serial_test_derive\",\"req\":\"~3.3.1\"}],\"features\":{\"async\":[\"dep:futures-executor\",\"dep:futures-util\",\"serial_test_derive/async\"],\"default\":[\"logging\",\"async\"],\"docsrs\":[\"dep:document-features\"],\"file_locks\":[\"dep:fslock\"],\"logging\":[\"dep:log\"],\"test_logging\":[\"logging\",\"dep:env_logger\",\"serial_test_derive/test_logging\"]}}", + "serial_test_derive_3.3.1": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\">=0.6.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"prettyplease\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"proc-macro\"],\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"full\",\"printing\",\"parsing\",\"clone-impls\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"async\":[],\"default\":[],\"file_locks\":[],\"test_logging\":[]}}", "sha1_0.10.6": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"cpufeatures\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_arch = \\\"aarch64\\\", target_arch = \\\"x86\\\", target_arch = \\\"x86_64\\\"))\"},{\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.2.2\"},{\"name\":\"sha1-asm\",\"optional\":true,\"req\":\"^0.5\",\"target\":\"cfg(any(target_arch = \\\"aarch64\\\", target_arch = \\\"x86\\\", target_arch = \\\"x86_64\\\"))\"}],\"features\":{\"asm\":[\"sha1-asm\"],\"compress\":[],\"default\":[\"std\"],\"force-soft\":[],\"loongarch64_asm\":[],\"oid\":[\"digest/oid\"],\"std\":[\"digest/std\"]}}", "sha1_smol_1.0.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"openssl\",\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.4\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"alloc\":[],\"std\":[\"alloc\"]}}", "sha2_0.10.9": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"cpufeatures\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_arch = \\\"aarch64\\\", target_arch = \\\"x86_64\\\", target_arch = \\\"x86\\\"))\"},{\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"features\":[\"dev\"],\"kind\":\"dev\",\"name\":\"digest\",\"req\":\"^0.10.7\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.2.2\"},{\"name\":\"sha2-asm\",\"optional\":true,\"req\":\"^0.6.1\",\"target\":\"cfg(any(target_arch = \\\"aarch64\\\", target_arch = \\\"x86_64\\\", target_arch = \\\"x86\\\"))\"}],\"features\":{\"asm\":[\"sha2-asm\"],\"asm-aarch64\":[\"asm\"],\"compress\":[],\"default\":[\"std\"],\"force-soft\":[],\"force-soft-compact\":[],\"loongarch64_asm\":[],\"oid\":[\"digest/oid\"],\"std\":[\"digest/std\"]}}", "sharded-slab_0.1.7": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"indexmap\",\"req\":\"^1\"},{\"name\":\"lazy_static\",\"req\":\"^1\"},{\"features\":[\"checkpoint\"],\"name\":\"loom\",\"optional\":true,\"req\":\"^0.5\",\"target\":\"cfg(loom)\"},{\"features\":[\"checkpoint\"],\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.5\",\"target\":\"cfg(loom)\"},{\"kind\":\"dev\",\"name\":\"memory-stats\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"slab\",\"req\":\"^0.4.2\"}],\"features\":{}}", "shared_library_0.1.9": "{\"dependencies\":[{\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"libc\",\"req\":\"^0.2\"}],\"features\":{}}", - "shell-words_1.1.0": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "shell-words_1.1.1": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "shlex_1.3.0": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}", - "signal-hook-mio_0.2.4": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"~0.2\"},{\"name\":\"mio-0_6\",\"optional\":true,\"package\":\"mio\",\"req\":\"~0.6\"},{\"features\":[\"os-util\",\"uds\"],\"name\":\"mio-0_7\",\"optional\":true,\"package\":\"mio\",\"req\":\"~0.7\"},{\"features\":[\"os-util\",\"os-poll\",\"uds\"],\"kind\":\"dev\",\"name\":\"mio-0_7\",\"package\":\"mio\",\"req\":\"~0.7\"},{\"features\":[\"net\",\"os-ext\"],\"name\":\"mio-0_8\",\"optional\":true,\"package\":\"mio\",\"req\":\"~0.8\"},{\"features\":[\"net\",\"os-ext\"],\"name\":\"mio-1_0\",\"optional\":true,\"package\":\"mio\",\"req\":\"~1.0\"},{\"name\":\"mio-uds\",\"optional\":true,\"req\":\"~0.6\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"~0.5\"},{\"name\":\"signal-hook\",\"req\":\"~0.3\"}],\"features\":{\"support-v0_6\":[\"mio-0_6\",\"mio-uds\"],\"support-v0_7\":[\"mio-0_7\"],\"support-v0_8\":[\"mio-0_8\"],\"support-v1_0\":[\"mio-1_0\"]}}", - "signal-hook-registry_1.4.5": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"signal-hook\",\"req\":\"~0.3\"}],\"features\":{}}", + "signal-hook-mio_0.2.5": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"~0.2\"},{\"name\":\"mio-0_6\",\"optional\":true,\"package\":\"mio\",\"req\":\"~0.6\"},{\"features\":[\"os-util\",\"uds\"],\"name\":\"mio-0_7\",\"optional\":true,\"package\":\"mio\",\"req\":\"~0.7\"},{\"features\":[\"os-util\",\"os-poll\",\"uds\"],\"kind\":\"dev\",\"name\":\"mio-0_7\",\"package\":\"mio\",\"req\":\"~0.7\"},{\"features\":[\"net\",\"os-ext\"],\"name\":\"mio-0_8\",\"optional\":true,\"package\":\"mio\",\"req\":\"~0.8\"},{\"features\":[\"net\",\"os-ext\"],\"name\":\"mio-1_0\",\"optional\":true,\"package\":\"mio\",\"req\":\"^1.0\"},{\"name\":\"mio-uds\",\"optional\":true,\"req\":\"~0.6\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"~3\"},{\"name\":\"signal-hook\",\"req\":\"~0.3\"}],\"features\":{\"support-v0_6\":[\"mio-0_6\",\"mio-uds\"],\"support-v0_7\":[\"mio-0_7\"],\"support-v0_8\":[\"mio-0_8\"],\"support-v1_0\":[\"mio-1_0\"]}}", + "signal-hook-registry_1.4.8": "{\"dependencies\":[{\"name\":\"errno\",\"req\":\">=0.2, <0.4\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"signal-hook\",\"req\":\"~0.3\"}],\"features\":{}}", "signal-hook_0.3.18": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^0.7\"},{\"name\":\"signal-hook-registry\",\"req\":\"^1.4\"}],\"features\":{\"channel\":[],\"default\":[\"channel\",\"iterator\"],\"extended-siginfo\":[\"channel\",\"iterator\",\"extended-siginfo-raw\"],\"extended-siginfo-raw\":[\"cc\"],\"iterator\":[\"channel\"]}}", "signature_2.2.0": "{\"dependencies\":[{\"name\":\"derive\",\"optional\":true,\"package\":\"signature_derive\",\"req\":\"^2\"},{\"default_features\":false,\"name\":\"digest\",\"optional\":true,\"req\":\"^0.10.6\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"rand_core\",\"optional\":true,\"req\":\"^0.6.4\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10\"}],\"features\":{\"alloc\":[],\"std\":[\"alloc\",\"rand_core?/std\"]}}", - "simd-adler32_0.3.7": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"adler\",\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"adler32\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"}],\"features\":{\"const-generics\":[],\"default\":[\"std\",\"const-generics\"],\"nightly\":[],\"std\":[]}}", + "simd-adler32_0.3.8": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"adler\",\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"adler32\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"}],\"features\":{\"const-generics\":[],\"default\":[\"std\",\"const-generics\"],\"nightly\":[],\"std\":[]}}", "simdutf8_0.1.5": "{\"dependencies\":[],\"features\":{\"aarch64_neon\":[],\"aarch64_neon_prefetch\":[],\"default\":[\"std\"],\"hints\":[],\"public_imp\":[],\"std\":[]}}", "similar_2.7.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"bstr\",\"optional\":true,\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"console\",\"req\":\"^0.15.0\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.10.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.130\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.68\"},{\"name\":\"unicode-segmentation\",\"optional\":true,\"req\":\"^1.7.1\"},{\"name\":\"web-time\",\"optional\":true,\"req\":\"^1.1\"}],\"features\":{\"bytes\":[\"bstr\",\"text\"],\"default\":[\"text\"],\"inline\":[\"text\"],\"text\":[],\"unicode\":[\"text\",\"unicode-segmentation\",\"bstr?/unicode\",\"bstr?/std\"],\"wasm32_web_time\":[\"web-time\"]}}", - "siphasher_1.0.1": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"serde_no_std\":[\"serde/alloc\"],\"serde_std\":[\"std\",\"serde/std\"],\"std\":[]}}", - "slab_0.4.11": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.95\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "siphasher_1.0.2": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"serde_no_std\":[\"serde/alloc\"],\"serde_std\":[\"std\",\"serde/std\"],\"std\":[]}}", + "slab_0.4.12": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.95\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "smallvec_1.15.1": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"name\":\"bincode\",\"optional\":true,\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"bincode1\",\"package\":\"bincode\",\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"debugger_test\",\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"debugger_test_parser\",\"req\":\"^0.1.0\"},{\"default_features\":false,\"name\":\"malloc_size_of\",\"optional\":true,\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"name\":\"unty\",\"optional\":true,\"req\":\"^0.0.4\"}],\"features\":{\"const_generics\":[],\"const_new\":[\"const_generics\"],\"debugger_visualizer\":[],\"drain_filter\":[],\"drain_keep_rest\":[\"drain_filter\"],\"impl_bincode\":[\"bincode\",\"unty\"],\"may_dangle\":[],\"specialization\":[],\"union\":[],\"write\":[]}}", "smawk_0.3.2": "{\"dependencies\":[{\"name\":\"ndarray\",\"optional\":true,\"req\":\"^0.15.4\"},{\"kind\":\"dev\",\"name\":\"num-traits\",\"req\":\"^0.2.14\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.4\"},{\"kind\":\"dev\",\"name\":\"rand_chacha\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"version-sync\",\"req\":\"^0.9.4\"}],\"features\":{}}", "smol_str_0.3.5": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.3\"},{\"default_features\":false,\"name\":\"borsh\",\"optional\":true,\"req\":\"^1.4.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.5\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9.2\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"serde\":[\"dep:serde_core\"],\"std\":[\"serde_core?/std\",\"borsh?/std\"]}}", "socket2_0.5.10": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.171\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Networking_WinSock\",\"Win32_System_IO\",\"Win32_System_Threading\",\"Win32_System_WindowsProgramming\"],\"name\":\"windows-sys\",\"req\":\"^0.52\",\"target\":\"cfg(windows)\"}],\"features\":{\"all\":[]}}", - "socket2_0.6.1": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.172\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Networking_WinSock\",\"Win32_System_IO\",\"Win32_System_Threading\",\"Win32_System_WindowsProgramming\"],\"name\":\"windows-sys\",\"req\":\"^0.60\",\"target\":\"cfg(windows)\"}],\"features\":{\"all\":[]}}", + "socket2_0.6.2": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.172\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Networking_WinSock\",\"Win32_System_IO\",\"Win32_System_Threading\",\"Win32_System_WindowsProgramming\"],\"name\":\"windows-sys\",\"req\":\"^0.60\",\"target\":\"cfg(windows)\"}],\"features\":{\"all\":[]}}", "spin_0.9.8": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\"},{\"name\":\"lock_api_crate\",\"optional\":true,\"package\":\"lock_api\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"barrier\":[\"mutex\"],\"default\":[\"lock_api\",\"mutex\",\"spin_mutex\",\"rwlock\",\"once\",\"lazy\",\"barrier\"],\"fair_mutex\":[\"mutex\"],\"lazy\":[\"once\"],\"lock_api\":[\"lock_api_crate\"],\"mutex\":[],\"once\":[],\"portable_atomic\":[\"portable-atomic\"],\"rwlock\":[],\"spin_mutex\":[\"mutex\"],\"std\":[],\"ticket_mutex\":[\"mutex\"],\"use_ticket_mutex\":[\"mutex\",\"ticket_mutex\"]}}", "spki_0.7.3": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.2\"},{\"default_features\":false,\"name\":\"base64ct\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"oid\"],\"name\":\"der\",\"req\":\"^0.7.2\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"alloc\":[\"base64ct?/alloc\",\"der/alloc\"],\"arbitrary\":[\"std\",\"dep:arbitrary\",\"der/arbitrary\"],\"base64\":[\"dep:base64ct\"],\"fingerprint\":[\"sha2\"],\"pem\":[\"alloc\",\"der/pem\"],\"std\":[\"der/std\",\"alloc\"]}}", "sqlx-core_0.8.6": "{\"dependencies\":[{\"name\":\"async-io\",\"optional\":true,\"req\":\"^1.9.0\"},{\"name\":\"async-std\",\"optional\":true,\"req\":\"^1.12\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"base64\",\"req\":\"^0.22.0\"},{\"name\":\"bigdecimal\",\"optional\":true,\"req\":\"^0.4.0\"},{\"name\":\"bit-vec\",\"optional\":true,\"req\":\"^0.6.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bstr\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"bytes\",\"req\":\"^1.1.0\"},{\"default_features\":false,\"features\":[\"clock\"],\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.34\"},{\"name\":\"crc\",\"optional\":true,\"req\":\"^3\"},{\"name\":\"crossbeam-queue\",\"req\":\"^0.3.2\"},{\"name\":\"either\",\"req\":\"^1.6.1\"},{\"name\":\"event-listener\",\"req\":\"^5.2.0\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.19\"},{\"name\":\"futures-intrusive\",\"req\":\"^0.5.0\"},{\"name\":\"futures-io\",\"req\":\"^0.3.24\"},{\"default_features\":false,\"features\":[\"alloc\",\"sink\",\"io\"],\"name\":\"futures-util\",\"req\":\"^0.3.19\"},{\"name\":\"hashbrown\",\"req\":\"^0.15.0\"},{\"name\":\"hashlink\",\"req\":\"^0.10.0\"},{\"name\":\"indexmap\",\"req\":\"^2.0\"},{\"name\":\"ipnet\",\"optional\":true,\"req\":\"^2.3.0\"},{\"name\":\"ipnetwork\",\"optional\":true,\"req\":\"^0.20.0\"},{\"default_features\":false,\"name\":\"log\",\"req\":\"^0.4.18\"},{\"name\":\"mac_address\",\"optional\":true,\"req\":\"^1.1.5\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2.4.1\"},{\"name\":\"native-tls\",\"optional\":true,\"req\":\"^0.2.10\"},{\"name\":\"once_cell\",\"req\":\"^1.9.0\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1.0\"},{\"name\":\"regex\",\"optional\":true,\"req\":\"^1.5.5\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rust_decimal\",\"optional\":true,\"req\":\"^1.26.1\"},{\"default_features\":false,\"features\":[\"std\",\"tls12\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.15\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8.0\"},{\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.132\"},{\"features\":[\"raw_value\"],\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.73\"},{\"default_features\":false,\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10.0\"},{\"name\":\"smallvec\",\"req\":\"^1.7.0\"},{\"default_features\":false,\"features\":[\"postgres\",\"sqlite\",\"mysql\",\"migrate\",\"macros\",\"time\",\"uuid\"],\"kind\":\"dev\",\"name\":\"sqlx\",\"req\":\"=0.8.6\"},{\"name\":\"thiserror\",\"req\":\"^2.0.0\"},{\"features\":[\"formatting\",\"parsing\",\"macros\"],\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.36\"},{\"default_features\":false,\"features\":[\"time\",\"net\",\"sync\",\"fs\",\"io-util\",\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"fs\"],\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1.8\"},{\"features\":[\"log\"],\"name\":\"tracing\",\"req\":\"^0.1.37\"},{\"name\":\"url\",\"req\":\"^2.2.2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.1.2\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^0.26\"}],\"features\":{\"_rt-async-std\":[\"async-std\",\"async-io\"],\"_rt-tokio\":[\"tokio\",\"tokio-stream\"],\"_tls-native-tls\":[\"native-tls\"],\"_tls-none\":[],\"_tls-rustls\":[\"rustls\"],\"_tls-rustls-aws-lc-rs\":[\"_tls-rustls\",\"rustls/aws-lc-rs\",\"webpki-roots\"],\"_tls-rustls-ring-native-roots\":[\"_tls-rustls\",\"rustls/ring\",\"rustls-native-certs\"],\"_tls-rustls-ring-webpki\":[\"_tls-rustls\",\"rustls/ring\",\"webpki-roots\"],\"any\":[],\"default\":[],\"json\":[\"serde\",\"serde_json\"],\"migrate\":[\"sha2\",\"crc\"],\"offline\":[\"serde\",\"either/serde\"]}}", @@ -1158,7 +1211,7 @@ "sqlx-sqlite_0.8.6": "{\"dependencies\":[{\"name\":\"atoi\",\"req\":\"^2.0\"},{\"default_features\":false,\"features\":[\"std\",\"clock\"],\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.34\"},{\"default_features\":false,\"features\":[\"async\"],\"name\":\"flume\",\"req\":\"^0.11.0\"},{\"default_features\":false,\"features\":[\"sink\",\"alloc\",\"std\"],\"name\":\"futures-channel\",\"req\":\"^0.3.19\"},{\"default_features\":false,\"name\":\"futures-core\",\"req\":\"^0.3.19\"},{\"name\":\"futures-executor\",\"req\":\"^0.3.19\"},{\"name\":\"futures-intrusive\",\"req\":\"^0.5.0\"},{\"default_features\":false,\"features\":[\"alloc\",\"sink\"],\"name\":\"futures-util\",\"req\":\"^0.3.19\"},{\"default_features\":false,\"features\":[\"pkg-config\",\"vcpkg\",\"unlock_notify\"],\"name\":\"libsqlite3-sys\",\"req\":\"^0.30.1\"},{\"name\":\"log\",\"req\":\"^0.4.18\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1.0\"},{\"name\":\"regex\",\"optional\":true,\"req\":\"^1.5.5\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.145\"},{\"name\":\"serde_urlencoded\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"macros\",\"runtime-tokio\",\"tls-none\",\"sqlite\"],\"kind\":\"dev\",\"name\":\"sqlx\",\"req\":\"=0.8.6\"},{\"name\":\"sqlx-core\",\"req\":\"=0.8.6\"},{\"name\":\"thiserror\",\"req\":\"^2.0.0\"},{\"features\":[\"formatting\",\"parsing\",\"macros\"],\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.36\"},{\"features\":[\"log\"],\"name\":\"tracing\",\"req\":\"^0.1.37\"},{\"name\":\"url\",\"req\":\"^2.2.2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.1.2\"}],\"features\":{\"any\":[\"sqlx-core/any\"],\"bundled\":[\"libsqlite3-sys/bundled\"],\"chrono\":[\"dep:chrono\",\"sqlx-core/chrono\"],\"json\":[\"sqlx-core/json\",\"serde\"],\"migrate\":[\"sqlx-core/migrate\"],\"offline\":[\"sqlx-core/offline\",\"serde\"],\"preupdate-hook\":[\"libsqlite3-sys/preupdate_hook\"],\"regexp\":[\"dep:regex\"],\"time\":[\"dep:time\",\"sqlx-core/time\"],\"unbundled\":[\"libsqlite3-sys/buildtime_bindgen\"],\"uuid\":[\"dep:uuid\",\"sqlx-core/uuid\"]}}", "sqlx_0.8.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.52\"},{\"features\":[\"attributes\"],\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.12\"},{\"features\":[\"async_tokio\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"kind\":\"dev\",\"name\":\"dotenvy\",\"req\":\"^0.15.0\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.19\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"libsqlite3-sys\",\"req\":\"^0.30.1\"},{\"features\":[\"bundled-sqlcipher\"],\"kind\":\"dev\",\"name\":\"libsqlite3-sys\",\"req\":\"^0.30.1\",\"target\":\"cfg(sqlite_test_sqlcipher)\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1.0.6\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.4\"},{\"kind\":\"dev\",\"name\":\"rand_xoshiro\",\"req\":\"^0.6.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.132\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.73\"},{\"features\":[\"offline\",\"migrate\"],\"name\":\"sqlx-core\",\"req\":\"=0.8.6\"},{\"name\":\"sqlx-macros\",\"optional\":true,\"req\":\"=0.8.6\"},{\"name\":\"sqlx-mysql\",\"optional\":true,\"req\":\"=0.8.6\"},{\"name\":\"sqlx-postgres\",\"optional\":true,\"req\":\"=0.8.6\"},{\"name\":\"sqlx-sqlite\",\"optional\":true,\"req\":\"=0.8.6\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.10.1\"},{\"kind\":\"dev\",\"name\":\"time_\",\"package\":\"time\",\"req\":\"^0.3.2\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.15.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.53\"},{\"kind\":\"dev\",\"name\":\"url\",\"req\":\"^2.2.2\"}],\"features\":{\"_rt-async-std\":[],\"_rt-tokio\":[],\"_sqlite\":[],\"_unstable-all-types\":[\"bigdecimal\",\"rust_decimal\",\"json\",\"time\",\"chrono\",\"ipnet\",\"ipnetwork\",\"mac_address\",\"uuid\",\"bit-vec\",\"bstr\"],\"all-databases\":[\"mysql\",\"sqlite\",\"postgres\",\"any\"],\"any\":[\"sqlx-core/any\",\"sqlx-mysql?/any\",\"sqlx-postgres?/any\",\"sqlx-sqlite?/any\"],\"bigdecimal\":[\"sqlx-core/bigdecimal\",\"sqlx-macros?/bigdecimal\",\"sqlx-mysql?/bigdecimal\",\"sqlx-postgres?/bigdecimal\"],\"bit-vec\":[\"sqlx-core/bit-vec\",\"sqlx-macros?/bit-vec\",\"sqlx-postgres?/bit-vec\"],\"bstr\":[\"sqlx-core/bstr\"],\"chrono\":[\"sqlx-core/chrono\",\"sqlx-macros?/chrono\",\"sqlx-mysql?/chrono\",\"sqlx-postgres?/chrono\",\"sqlx-sqlite?/chrono\"],\"default\":[\"any\",\"macros\",\"migrate\",\"json\"],\"derive\":[\"sqlx-macros/derive\"],\"ipnet\":[\"sqlx-core/ipnet\",\"sqlx-macros?/ipnet\",\"sqlx-postgres?/ipnet\"],\"ipnetwork\":[\"sqlx-core/ipnetwork\",\"sqlx-macros?/ipnetwork\",\"sqlx-postgres?/ipnetwork\"],\"json\":[\"sqlx-core/json\",\"sqlx-macros?/json\",\"sqlx-mysql?/json\",\"sqlx-postgres?/json\",\"sqlx-sqlite?/json\"],\"mac_address\":[\"sqlx-core/mac_address\",\"sqlx-macros?/mac_address\",\"sqlx-postgres?/mac_address\"],\"macros\":[\"derive\",\"sqlx-macros/macros\"],\"migrate\":[\"sqlx-core/migrate\",\"sqlx-macros?/migrate\",\"sqlx-mysql?/migrate\",\"sqlx-postgres?/migrate\",\"sqlx-sqlite?/migrate\"],\"mysql\":[\"sqlx-mysql\",\"sqlx-macros?/mysql\"],\"postgres\":[\"sqlx-postgres\",\"sqlx-macros?/postgres\"],\"regexp\":[\"sqlx-sqlite?/regexp\"],\"runtime-async-std\":[\"_rt-async-std\",\"sqlx-core/_rt-async-std\",\"sqlx-macros?/_rt-async-std\"],\"runtime-async-std-native-tls\":[\"runtime-async-std\",\"tls-native-tls\"],\"runtime-async-std-rustls\":[\"runtime-async-std\",\"tls-rustls-ring\"],\"runtime-tokio\":[\"_rt-tokio\",\"sqlx-core/_rt-tokio\",\"sqlx-macros?/_rt-tokio\"],\"runtime-tokio-native-tls\":[\"runtime-tokio\",\"tls-native-tls\"],\"runtime-tokio-rustls\":[\"runtime-tokio\",\"tls-rustls-ring\"],\"rust_decimal\":[\"sqlx-core/rust_decimal\",\"sqlx-macros?/rust_decimal\",\"sqlx-mysql?/rust_decimal\",\"sqlx-postgres?/rust_decimal\"],\"sqlite\":[\"_sqlite\",\"sqlx-sqlite/bundled\",\"sqlx-macros?/sqlite\"],\"sqlite-preupdate-hook\":[\"sqlx-sqlite/preupdate-hook\"],\"sqlite-unbundled\":[\"_sqlite\",\"sqlx-sqlite/unbundled\",\"sqlx-macros?/sqlite-unbundled\"],\"time\":[\"sqlx-core/time\",\"sqlx-macros?/time\",\"sqlx-mysql?/time\",\"sqlx-postgres?/time\",\"sqlx-sqlite?/time\"],\"tls-native-tls\":[\"sqlx-core/_tls-native-tls\",\"sqlx-macros?/_tls-native-tls\"],\"tls-none\":[],\"tls-rustls\":[\"tls-rustls-ring\"],\"tls-rustls-aws-lc-rs\":[\"sqlx-core/_tls-rustls-aws-lc-rs\",\"sqlx-macros?/_tls-rustls-aws-lc-rs\"],\"tls-rustls-ring\":[\"tls-rustls-ring-webpki\"],\"tls-rustls-ring-native-roots\":[\"sqlx-core/_tls-rustls-ring-native-roots\",\"sqlx-macros?/_tls-rustls-ring-native-roots\"],\"tls-rustls-ring-webpki\":[\"sqlx-core/_tls-rustls-ring-webpki\",\"sqlx-macros?/_tls-rustls-ring-webpki\"],\"uuid\":[\"sqlx-core/uuid\",\"sqlx-macros?/uuid\",\"sqlx-mysql?/uuid\",\"sqlx-postgres?/uuid\",\"sqlx-sqlite?/uuid\"]}}", "sse-stream_0.2.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"features\":[\"tracing\"],\"kind\":\"dev\",\"name\":\"axum\",\"req\":\"^0.8\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"http-body\",\"req\":\"^1\"},{\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"features\":[\"client\",\"http1\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1\"},{\"features\":[\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"features\":[\"stream\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"io\"],\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"}],\"features\":{\"default\":[],\"tracing\":[\"dep:tracing\"]}}", - "stable_deref_trait_1.2.0": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", + "stable_deref_trait_1.2.1": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "starlark_0.13.0": "{\"dependencies\":[{\"features\":[\"bumpalo\",\"num-bigint\"],\"name\":\"allocative\",\"req\":\"^0.3.4\"},{\"name\":\"anyhow\",\"req\":\"^1.0.65\"},{\"name\":\"bumpalo\",\"req\":\"^3.8\"},{\"name\":\"cmp_any\",\"req\":\"^0.8.1\"},{\"name\":\"debugserver-types\",\"req\":\"^0.5.0\"},{\"name\":\"derivative\",\"req\":\"^2.2\"},{\"features\":[\"full\"],\"name\":\"derive_more\",\"req\":\"^1.0.0\"},{\"name\":\"display_container\",\"req\":\"^0.9.0\"},{\"name\":\"dupe\",\"req\":\"^0.9.0\"},{\"name\":\"either\",\"req\":\"^1.8\"},{\"name\":\"erased-serde\",\"req\":\"^0.3.12\"},{\"features\":[\"raw\"],\"name\":\"hashbrown\",\"req\":\"^0.14.3\"},{\"name\":\"inventory\",\"req\":\"^0.3.8\"},{\"name\":\"itertools\",\"req\":\"^0.13.0\"},{\"name\":\"maplit\",\"req\":\"^1.0.2\"},{\"name\":\"memoffset\",\"req\":\"^0.6.4\"},{\"name\":\"num-bigint\",\"req\":\"^0.4.3\"},{\"name\":\"num-traits\",\"req\":\"^0.2\"},{\"name\":\"once_cell\",\"req\":\"^1.8\"},{\"name\":\"paste\",\"req\":\"^1.0\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.4\"},{\"name\":\"ref-cast\",\"req\":\"^1.0.18\"},{\"name\":\"regex\",\"req\":\"^1.5.4\"},{\"name\":\"rustyline\",\"req\":\"^14.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"starlark_derive\",\"req\":\"^0.13.0\"},{\"name\":\"starlark_map\",\"req\":\"^0.13.0\"},{\"name\":\"starlark_syntax\",\"req\":\"^0.13.0\"},{\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"name\":\"strsim\",\"req\":\"^0.10.0\"},{\"name\":\"textwrap\",\"req\":\"^0.11\"},{\"name\":\"thiserror\",\"req\":\"^1.0.36\"}],\"features\":{}}", "starlark_derive_0.13.0": "{\"dependencies\":[{\"name\":\"dupe\",\"req\":\"^0.9.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"extra-traits\",\"full\",\"visit\",\"visit-mut\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", "starlark_map_0.13.0": "{\"dependencies\":[{\"features\":[\"hashbrown\"],\"name\":\"allocative\",\"req\":\"^0.3.4\"},{\"name\":\"dupe\",\"req\":\"^0.9.0\"},{\"name\":\"equivalent\",\"req\":\"^1.0.0\"},{\"name\":\"fxhash\",\"req\":\"^0.2.1\"},{\"features\":[\"raw\"],\"name\":\"hashbrown\",\"req\":\"^0.14.3\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.48\"}],\"features\":{}}", @@ -1177,17 +1230,17 @@ "supports-color_2.1.0": "{\"dependencies\":[{\"name\":\"is-terminal\",\"req\":\"^0.4.0\"},{\"name\":\"is_ci\",\"req\":\"^1.1.1\"}],\"features\":{}}", "supports-color_3.0.2": "{\"dependencies\":[{\"name\":\"is_ci\",\"req\":\"^1.2.0\"}],\"features\":{}}", "syn_1.0.109": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.46\"},{\"default_features\":false,\"name\":\"quote\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.0\"},{\"features\":[\"blocking\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.11\"},{\"kind\":\"dev\",\"name\":\"syn-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"tar\",\"req\":\"^0.4.16\"},{\"kind\":\"dev\",\"name\":\"termcolor\",\"req\":\"^1.0\"},{\"name\":\"unicode-ident\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.1\"}],\"features\":{\"clone-impls\":[],\"default\":[\"derive\",\"parsing\",\"printing\",\"clone-impls\",\"proc-macro\"],\"derive\":[],\"extra-traits\":[],\"fold\":[],\"full\":[],\"parsing\":[],\"printing\":[\"quote\"],\"proc-macro\":[\"proc-macro2/proc-macro\",\"quote/proc-macro\"],\"test\":[\"syn-test-suite/all-features\"],\"visit\":[],\"visit-mut\":[]}}", - "syn_2.0.104": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.91\"},{\"default_features\":false,\"name\":\"quote\",\"optional\":true,\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1\"},{\"features\":[\"blocking\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"syn-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"tar\",\"req\":\"^0.4.16\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"termcolor\",\"req\":\"^1\"},{\"name\":\"unicode-ident\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.3.2\",\"target\":\"cfg(not(miri))\"}],\"features\":{\"clone-impls\":[],\"default\":[\"derive\",\"parsing\",\"printing\",\"clone-impls\",\"proc-macro\"],\"derive\":[],\"extra-traits\":[],\"fold\":[],\"full\":[],\"parsing\":[],\"printing\":[\"dep:quote\"],\"proc-macro\":[\"proc-macro2/proc-macro\",\"quote?/proc-macro\"],\"test\":[\"syn-test-suite/all-features\"],\"visit\":[],\"visit-mut\":[]}}", + "syn_2.0.114": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.91\"},{\"default_features\":false,\"name\":\"quote\",\"optional\":true,\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1\"},{\"features\":[\"blocking\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.13\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"syn-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"tar\",\"req\":\"^0.4.16\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"termcolor\",\"req\":\"^1\"},{\"name\":\"unicode-ident\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.3.2\",\"target\":\"cfg(not(miri))\"}],\"features\":{\"clone-impls\":[],\"default\":[\"derive\",\"parsing\",\"printing\",\"clone-impls\",\"proc-macro\"],\"derive\":[],\"extra-traits\":[],\"fold\":[],\"full\":[],\"parsing\":[],\"printing\":[\"dep:quote\"],\"proc-macro\":[\"proc-macro2/proc-macro\",\"quote?/proc-macro\"],\"test\":[\"syn-test-suite/all-features\"],\"visit\":[],\"visit-mut\":[]}}", "sync_wrapper_1.0.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"}],\"features\":{\"futures\":[\"futures-core\"]}}", "synstructure_0.13.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"derive\",\"parsing\",\"printing\",\"clone-impls\",\"visit\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"synstructure_test_traits\",\"req\":\"^0.1\"}],\"features\":{\"default\":[\"proc-macro\"],\"proc-macro\":[\"proc-macro2/proc-macro\",\"syn/proc-macro\",\"quote/proc-macro\"]}}", "sys-locale_0.3.2": "{\"dependencies\":[{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", not(unix)))\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", not(unix)))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", not(unix)))\"},{\"features\":[\"Window\",\"WorkerGlobalScope\",\"Navigator\",\"WorkerNavigator\"],\"name\":\"web-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", not(unix)))\"}],\"features\":{\"js\":[\"js-sys\",\"wasm-bindgen\",\"web-sys\"]}}", "system-configuration-sys_0.6.0": "{\"dependencies\":[{\"name\":\"core-foundation-sys\",\"req\":\"^0.8\"},{\"name\":\"libc\",\"req\":\"^0.2.149\"}],\"features\":{}}", "system-configuration_0.6.1": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"name\":\"core-foundation\",\"req\":\"^0.9\"},{\"name\":\"system-configuration-sys\",\"req\":\"^0.6\"}],\"features\":{}}", "tagptr_0.2.0": "{\"dependencies\":[],\"features\":{}}", - "tempfile_3.23.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"fastrand\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.0\",\"target\":\"cfg(any(unix, windows, target_os = \\\"wasi\\\"))\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"once_cell\",\"req\":\"^1.19.0\"},{\"features\":[\"fs\"],\"name\":\"rustix\",\"req\":\"^1.0.0\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\"))\"},{\"features\":[\"Win32_Storage_FileSystem\",\"Win32_Foundation\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"getrandom\"],\"nightly\":[]}}", + "tempfile_3.24.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"fastrand\",\"req\":\"^2.1.1\"},{\"default_features\":false,\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.0\",\"target\":\"cfg(any(unix, windows, target_os = \\\"wasi\\\"))\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"once_cell\",\"req\":\"^1.19.0\"},{\"features\":[\"fs\"],\"name\":\"rustix\",\"req\":\"^1.1.3\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\"))\"},{\"features\":[\"Win32_Storage_FileSystem\",\"Win32_Foundation\"],\"name\":\"windows-sys\",\"req\":\">=0.52, <0.62\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"getrandom\"],\"nightly\":[]}}", "term_0.7.0": "{\"dependencies\":[{\"name\":\"dirs-next\",\"req\":\"^2\"},{\"name\":\"rustversion\",\"req\":\"^1\",\"target\":\"cfg(windows)\"},{\"features\":[\"consoleapi\",\"wincon\",\"handleapi\",\"fileapi\"],\"name\":\"winapi\",\"req\":\"^0.3\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[]}}", "termcolor_1.4.1": "{\"dependencies\":[{\"name\":\"winapi-util\",\"req\":\"^0.1.3\",\"target\":\"cfg(windows)\"}],\"features\":{}}", - "terminal_size_0.4.2": "{\"dependencies\":[{\"features\":[\"termios\"],\"name\":\"rustix\",\"req\":\"^1.0.1\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Console\"],\"name\":\"windows-sys\",\"req\":\"^0.59.0\",\"target\":\"cfg(windows)\"}],\"features\":{}}", + "terminal_size_0.4.3": "{\"dependencies\":[{\"features\":[\"termios\"],\"name\":\"rustix\",\"req\":\"^1.0.1\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Console\"],\"name\":\"windows-sys\",\"req\":\"^0.60.0\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "termtree_0.5.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.10\"}],\"features\":{}}", "test-case-core_3.3.1": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{\"with-regex\":[]}}", "test-case-macros_3.3.1": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"full\",\"extra-traits\",\"parsing\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"default_features\":false,\"name\":\"test-case-core\",\"req\":\"^3.2.1\"}],\"features\":{\"with-regex\":[\"test-case-core/with-regex\"]}}", @@ -1197,65 +1250,68 @@ "textwrap_0.11.0": "{\"dependencies\":[{\"features\":[\"embed_all\"],\"name\":\"hyphenation\",\"optional\":true,\"req\":\"^0.7.1\"},{\"kind\":\"dev\",\"name\":\"lipsum\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"rand_xorshift\",\"req\":\"^0.1\"},{\"name\":\"term_size\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"unicode-width\",\"req\":\"^0.1.3\"},{\"kind\":\"dev\",\"name\":\"version-sync\",\"req\":\"^0.6\"}],\"features\":{}}", "textwrap_0.16.2": "{\"dependencies\":[{\"features\":[\"embed_en-us\"],\"name\":\"hyphenation\",\"optional\":true,\"req\":\"^0.8.4\"},{\"name\":\"smawk\",\"optional\":true,\"req\":\"^0.3.2\"},{\"name\":\"terminal_size\",\"optional\":true,\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"termion\",\"req\":\"^4.0.2\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"unic-emoji-char\",\"req\":\"^0.9.0\"},{\"name\":\"unicode-linebreak\",\"optional\":true,\"req\":\"^0.1.5\"},{\"name\":\"unicode-width\",\"optional\":true,\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"version-sync\",\"req\":\"^0.9.5\"}],\"features\":{\"default\":[\"unicode-linebreak\",\"unicode-width\",\"smawk\"]}}", "thiserror-impl_1.0.69": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"name\":\"syn\",\"req\":\"^2.0.87\"}],\"features\":{}}", - "thiserror-impl_2.0.17": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"name\":\"syn\",\"req\":\"^2.0.87\"}],\"features\":{}}", + "thiserror-impl_2.0.18": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.74\"},{\"name\":\"quote\",\"req\":\"^1.0.35\"},{\"name\":\"syn\",\"req\":\"^2.0.87\"}],\"features\":{}}", "thiserror_1.0.69": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.73\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"name\":\"thiserror-impl\",\"req\":\"=1.0.69\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.81\"}],\"features\":{}}", - "thiserror_2.0.17": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.73\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"name\":\"thiserror-impl\",\"req\":\"=2.0.17\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "thiserror_2.0.18": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.73\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.13\"},{\"name\":\"thiserror-impl\",\"req\":\"=2.0.18\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.108\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "thread_local_1.1.9": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"}],\"features\":{\"nightly\":[]}}", "tiff_0.10.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"crc32fast\",\"req\":\"^1.5\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.1\"},{\"name\":\"fax34\",\"optional\":true,\"package\":\"fax\",\"req\":\"^0.2.6\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0.20\"},{\"name\":\"half\",\"req\":\"^2.4.1\"},{\"name\":\"quick-error\",\"req\":\"^2.0.1\"},{\"name\":\"weezl\",\"optional\":true,\"req\":\"^0.1.10\"},{\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.13\"},{\"name\":\"zune-jpeg\",\"optional\":true,\"req\":\"^0.4.17\"}],\"features\":{\"default\":[\"deflate\",\"fax\",\"jpeg\",\"lzw\"],\"deflate\":[\"dep:flate2\"],\"fax\":[\"dep:fax34\"],\"jpeg\":[\"dep:zune-jpeg\"],\"lzw\":[\"dep:weezl\"],\"zstd\":[\"dep:zstd\"]}}", - "time-core_0.1.6": "{\"dependencies\":[],\"features\":{}}", - "time-macros_0.2.24": "{\"dependencies\":[{\"name\":\"num-conv\",\"req\":\"^0.1.0\"},{\"name\":\"time-core\",\"req\":\"=0.1.6\"}],\"features\":{\"formatting\":[],\"large-dates\":[],\"parsing\":[],\"serde\":[]}}", - "time_0.3.44": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\",\"target\":\"cfg(bench)\"},{\"features\":[\"powerfmt\"],\"name\":\"deranged\",\"req\":\"^0.5.2\"},{\"name\":\"itoa\",\"optional\":true,\"req\":\"^1.0.1\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.58\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.98\",\"target\":\"cfg(target_family = \\\"unix\\\")\"},{\"name\":\"num-conv\",\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"num-conv\",\"req\":\"^0.1.0\"},{\"name\":\"num_threads\",\"optional\":true,\"req\":\"^0.1.2\",\"target\":\"cfg(target_family = \\\"unix\\\")\"},{\"default_features\":false,\"name\":\"powerfmt\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"rand08\",\"optional\":true,\"package\":\"rand\",\"req\":\"^0.8.4\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"rand08\",\"package\":\"rand\",\"req\":\"^0.8.4\"},{\"default_features\":false,\"name\":\"rand09\",\"optional\":true,\"package\":\"rand\",\"req\":\"^0.9.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"rand09\",\"package\":\"rand\",\"req\":\"^0.9.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.23.0\"},{\"kind\":\"dev\",\"name\":\"rstest_reuse\",\"req\":\"^0.7.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.184\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.184\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.68\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.126\"},{\"name\":\"time-core\",\"req\":\"=0.1.6\"},{\"name\":\"time-macros\",\"optional\":true,\"req\":\"=0.2.24\"},{\"kind\":\"dev\",\"name\":\"time-macros\",\"req\":\"=0.2.24\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.102\",\"target\":\"cfg(__ui_tests)\"}],\"features\":{\"alloc\":[\"serde?/alloc\"],\"default\":[\"std\"],\"formatting\":[\"dep:itoa\",\"std\",\"time-macros?/formatting\"],\"large-dates\":[\"time-macros?/large-dates\"],\"local-offset\":[\"std\",\"dep:libc\",\"dep:num_threads\"],\"macros\":[\"dep:time-macros\"],\"parsing\":[\"time-macros?/parsing\"],\"quickcheck\":[\"dep:quickcheck\",\"alloc\",\"deranged/quickcheck\"],\"rand\":[\"rand08\",\"rand09\"],\"rand08\":[\"dep:rand08\",\"deranged/rand08\"],\"rand09\":[\"dep:rand09\",\"deranged/rand09\"],\"serde\":[\"dep:serde\",\"time-macros?/serde\",\"deranged/serde\"],\"serde-human-readable\":[\"serde\",\"formatting\",\"parsing\"],\"serde-well-known\":[\"serde\",\"formatting\",\"parsing\"],\"std\":[\"alloc\"],\"wasm-bindgen\":[\"dep:js-sys\"]}}", + "time-core_0.1.8": "{\"dependencies\":[],\"features\":{\"large-dates\":[]}}", + "time-macros_0.2.26": "{\"dependencies\":[{\"name\":\"num-conv\",\"req\":\"^0.2.0\"},{\"name\":\"time-core\",\"req\":\"=0.1.8\"}],\"features\":{\"formatting\":[],\"large-dates\":[],\"parsing\":[],\"serde\":[]}}", + "time_0.3.46": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8.1\",\"target\":\"cfg(bench)\"},{\"features\":[\"powerfmt\"],\"name\":\"deranged\",\"req\":\"^0.5.2\"},{\"name\":\"itoa\",\"optional\":true,\"req\":\"^1.0.1\"},{\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3.58\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.98\",\"target\":\"cfg(target_family = \\\"unix\\\")\"},{\"name\":\"num-conv\",\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"num-conv\",\"req\":\"^0.2.0\"},{\"name\":\"num_threads\",\"optional\":true,\"req\":\"^0.1.2\",\"target\":\"cfg(target_family = \\\"unix\\\")\"},{\"default_features\":false,\"name\":\"powerfmt\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"rand08\",\"optional\":true,\"package\":\"rand\",\"req\":\"^0.8.4\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"rand08\",\"package\":\"rand\",\"req\":\"^0.8.4\"},{\"default_features\":false,\"name\":\"rand09\",\"optional\":true,\"package\":\"rand\",\"req\":\"^0.9.2\"},{\"default_features\":false,\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand09\",\"package\":\"rand\",\"req\":\"^0.9.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.26.1\"},{\"kind\":\"dev\",\"name\":\"rstest_reuse\",\"req\":\"^0.7.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.184\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.68\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.126\"},{\"name\":\"time-core\",\"req\":\"=0.1.8\"},{\"name\":\"time-macros\",\"optional\":true,\"req\":\"=0.2.26\"},{\"kind\":\"dev\",\"name\":\"time-macros\",\"req\":\"=0.2.26\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.102\",\"target\":\"cfg(__ui_tests)\"}],\"features\":{\"alloc\":[\"serde_core?/alloc\"],\"default\":[\"std\"],\"formatting\":[\"dep:itoa\",\"std\",\"time-macros?/formatting\"],\"large-dates\":[\"time-core/large-dates\",\"time-macros?/large-dates\"],\"local-offset\":[\"std\",\"dep:libc\",\"dep:num_threads\"],\"macros\":[\"dep:time-macros\"],\"parsing\":[\"time-macros?/parsing\"],\"quickcheck\":[\"dep:quickcheck\",\"alloc\",\"deranged/quickcheck\"],\"rand\":[\"rand08\",\"rand09\"],\"rand08\":[\"dep:rand08\",\"deranged/rand08\"],\"rand09\":[\"dep:rand09\",\"deranged/rand09\"],\"serde\":[\"dep:serde_core\",\"time-macros?/serde\",\"deranged/serde\"],\"serde-human-readable\":[\"serde\",\"formatting\",\"parsing\"],\"serde-well-known\":[\"serde\",\"formatting\",\"parsing\"],\"std\":[\"alloc\"],\"wasm-bindgen\":[\"dep:js-sys\"]}}", "tiny-keccak_2.0.2": "{\"dependencies\":[{\"name\":\"crunchy\",\"req\":\"^0.2.2\"}],\"features\":{\"cshake\":[],\"default\":[],\"fips202\":[\"keccak\",\"shake\",\"sha3\"],\"k12\":[],\"keccak\":[],\"kmac\":[\"cshake\"],\"parallel_hash\":[\"cshake\"],\"sha3\":[],\"shake\":[],\"sp800\":[\"cshake\",\"kmac\",\"tuple_hash\"],\"tuple_hash\":[\"cshake\"]}}", "tiny_http_0.12.0": "{\"dependencies\":[{\"name\":\"ascii\",\"req\":\"^1.0\"},{\"name\":\"chunked_transfer\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"fdlimit\",\"req\":\"^0.1\"},{\"name\":\"httpdate\",\"req\":\"^1.0.2\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"rustc-serialize\",\"req\":\"^0.3\"},{\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.20\"},{\"name\":\"rustls-pemfile\",\"optional\":true,\"req\":\"^0.2.1\"},{\"kind\":\"dev\",\"name\":\"sha1\",\"req\":\"^0.6.0\"},{\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"default\":[],\"ssl\":[\"ssl-openssl\"],\"ssl-openssl\":[\"openssl\",\"zeroize\"],\"ssl-rustls\":[\"rustls\",\"rustls-pemfile\",\"zeroize\"]}}", - "tinystr_0.8.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2.3\"},{\"default_features\":false,\"features\":[\"use-std\"],\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.110\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"name\":\"zerovec\",\"optional\":true,\"req\":\"^0.11.1\"}],\"features\":{\"alloc\":[\"zerovec?/alloc\"],\"databake\":[\"dep:databake\"],\"default\":[\"alloc\"],\"serde\":[\"dep:serde\"],\"std\":[],\"zerovec\":[\"dep:zerovec\"]}}", + "tinystr_0.8.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2.3\"},{\"default_features\":false,\"features\":[\"use-std\"],\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"name\":\"zerovec\",\"optional\":true,\"req\":\"^0.11.3\"}],\"features\":{\"alloc\":[\"serde_core?/alloc\",\"zerovec?/alloc\"],\"databake\":[\"dep:databake\"],\"default\":[\"alloc\"],\"serde\":[\"dep:serde_core\"],\"std\":[],\"zerovec\":[\"dep:zerovec\"]}}", "tinyvec_1.10.0": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"name\":\"borsh\",\"optional\":true,\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"debugger_test\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"debugger_test_parser\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"generic-array\",\"optional\":true,\"req\":\"^1.1.1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"smallvec\",\"req\":\"^1\"},{\"name\":\"tinyvec_macros\",\"optional\":true,\"req\":\"^0.1\"}],\"features\":{\"alloc\":[\"tinyvec_macros\"],\"debugger_visualizer\":[],\"default\":[],\"experimental_write_impl\":[],\"grab_spare_slice\":[],\"latest_stable_rust\":[\"rustc_1_61\"],\"nightly_slice_partition_dedup\":[],\"real_blackbox\":[\"criterion/real_blackbox\"],\"rustc_1_40\":[],\"rustc_1_55\":[],\"rustc_1_57\":[],\"rustc_1_61\":[\"rustc_1_57\"],\"std\":[\"alloc\"]}}", "tinyvec_macros_0.1.1": "{\"dependencies\":[],\"features\":{}}", "tokio-graceful_0.2.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bytes\",\"req\":\"^1\",\"target\":\"cfg(not(loom))\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1\",\"target\":\"cfg(not(loom))\"},{\"features\":[\"server\",\"http1\",\"http2\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.0.1\",\"target\":\"cfg(not(loom))\"},{\"features\":[\"server\",\"server-auto\",\"http1\",\"http2\",\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1.1\",\"target\":\"cfg(not(loom))\"},{\"features\":[\"futures\",\"checkpoint\"],\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"slab\",\"req\":\"^0.4\"},{\"features\":[\"rt\",\"signal\",\"sync\",\"macros\",\"time\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"net\",\"rt-multi-thread\",\"io-util\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"}],\"features\":{}}", "tokio-macros_2.6.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0.0\"}],\"features\":{}}", "tokio-native-tls_0.3.1": "{\"dependencies\":[{\"name\":\"native-tls\",\"req\":\"^0.2\"},{\"name\":\"tokio\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"cfg-if\",\"req\":\"^0.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.6\"},{\"features\":[\"async-await\"],\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.4.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1\"},{\"features\":[\"macros\",\"rt\",\"rt-multi-thread\",\"io-util\",\"net\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.6.0\"},{\"kind\":\"dev\",\"name\":\"openssl\",\"req\":\"^0.10\",\"target\":\"cfg(all(not(target_os = \\\"macos\\\"), not(windows), not(target_os = \\\"ios\\\")))\"},{\"kind\":\"dev\",\"name\":\"security-framework\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_os = \\\"macos\\\", target_os = \\\"ios\\\"))\"},{\"kind\":\"dev\",\"name\":\"schannel\",\"req\":\"^0.1\",\"target\":\"cfg(windows)\"},{\"features\":[\"lmcons\",\"basetsd\",\"minwinbase\",\"minwindef\",\"ntdef\",\"sysinfoapi\",\"timezoneapi\",\"wincrypt\",\"winerror\"],\"kind\":\"dev\",\"name\":\"winapi\",\"req\":\"^0.3\",\"target\":\"cfg(windows)\"}],\"features\":{\"vendored\":[\"native-tls/vendored\"]}}", - "tokio-rustls_0.26.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"argh\",\"req\":\"^0.1.1\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.1\"},{\"features\":[\"pem\"],\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.13\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"req\":\"^0.23.22\"},{\"name\":\"tokio\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"webpki-roots\",\"req\":\"^0.26\"}],\"features\":{\"aws-lc-rs\":[\"aws_lc_rs\"],\"aws_lc_rs\":[\"rustls/aws_lc_rs\"],\"default\":[\"logging\",\"tls12\",\"aws_lc_rs\"],\"early-data\":[],\"fips\":[\"rustls/fips\"],\"logging\":[\"rustls/logging\"],\"ring\":[\"rustls/ring\"],\"tls12\":[\"rustls/tls12\"]}}", + "tokio-rustls_0.26.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"argh\",\"req\":\"^0.1.1\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.1\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.1\"},{\"features\":[\"pem\"],\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"req\":\"^0.23.27\"},{\"name\":\"tokio\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"webpki-roots\",\"req\":\"^1\"}],\"features\":{\"aws-lc-rs\":[\"aws_lc_rs\"],\"aws_lc_rs\":[\"rustls/aws_lc_rs\"],\"brotli\":[\"rustls/brotli\"],\"default\":[\"logging\",\"tls12\",\"aws_lc_rs\"],\"early-data\":[],\"fips\":[\"rustls/fips\"],\"logging\":[\"rustls/logging\"],\"ring\":[\"rustls/ring\"],\"tls12\":[\"rustls/tls12\"],\"zlib\":[\"rustls/zlib\"]}}", "tokio-stream_0.1.18": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-stream\",\"req\":\"^0.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"futures-core\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"^0.12.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1.15.0\"},{\"features\":[\"full\",\"test-util\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"},{\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7.0\"}],\"features\":{\"default\":[\"time\"],\"fs\":[\"tokio/fs\"],\"full\":[\"time\",\"net\",\"io-util\",\"fs\",\"sync\",\"signal\"],\"io-util\":[\"tokio/io-util\"],\"net\":[\"tokio/net\"],\"signal\":[\"tokio/signal\"],\"sync\":[\"tokio/sync\",\"tokio-util\"],\"time\":[\"tokio/time\"]}}", "tokio-test_0.4.5": "{\"dependencies\":[{\"name\":\"futures-core\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.0\"},{\"features\":[\"rt\",\"sync\",\"time\",\"test-util\"],\"name\":\"tokio\",\"req\":\"^1.2.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.2.0\"},{\"name\":\"tokio-stream\",\"req\":\"^0.1.1\"}],\"features\":{}}", "tokio-util_0.7.18": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-stream\",\"req\":\"^0.3.0\"},{\"name\":\"bytes\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.0\"},{\"name\":\"futures-core\",\"req\":\"^0.3.0\"},{\"name\":\"futures-io\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"futures-sink\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"futures-test\",\"req\":\"^0.3.5\"},{\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.0\"},{\"default_features\":false,\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15.0\"},{\"features\":[\"futures\",\"checkpoint\"],\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"kind\":\"dev\",\"name\":\"parking_lot\",\"req\":\"^0.12.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.4\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"req\":\"^1.44.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.29\"}],\"features\":{\"__docs_rs\":[\"futures-util\"],\"codec\":[],\"compat\":[\"futures-io\"],\"default\":[],\"full\":[\"codec\",\"compat\",\"io-util\",\"time\",\"net\",\"rt\",\"join-map\"],\"io\":[],\"io-util\":[\"io\",\"tokio/rt\",\"tokio/io-util\"],\"join-map\":[\"rt\",\"hashbrown\"],\"net\":[\"tokio/net\"],\"rt\":[\"tokio/rt\",\"tokio/sync\",\"futures-util\"],\"time\":[\"tokio/time\",\"slab\"]}}", "tokio_1.49.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-stream\",\"req\":\"^0.3\"},{\"name\":\"backtrace\",\"optional\":true,\"req\":\"^0.3.58\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1.2.1\"},{\"features\":[\"async-await\"],\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"futures-concurrency\",\"req\":\"^7.6.3\"},{\"kind\":\"dev\",\"name\":\"futures-test\",\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"io-uring\",\"optional\":true,\"req\":\"^0.7.6\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.168\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.168\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.168\",\"target\":\"cfg(unix)\"},{\"features\":[\"futures\",\"checkpoint\"],\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"default_features\":false,\"name\":\"mio\",\"optional\":true,\"req\":\"^1.0.1\"},{\"default_features\":false,\"features\":[\"os-poll\",\"os-ext\"],\"name\":\"mio\",\"optional\":true,\"req\":\"^1.0.1\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"features\":[\"tokio\"],\"kind\":\"dev\",\"name\":\"mio-aio\",\"req\":\"^1\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"kind\":\"dev\",\"name\":\"mockall\",\"req\":\"^0.13.0\"},{\"default_features\":false,\"features\":[\"aio\",\"fs\",\"socket\"],\"kind\":\"dev\",\"name\":\"nix\",\"req\":\"^0.29.0\",\"target\":\"cfg(unix)\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.11\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"signal-hook-registry\",\"optional\":true,\"req\":\"^1.1.1\",\"target\":\"cfg(unix)\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.9\",\"target\":\"cfg(all(tokio_unstable, target_os = \\\"linux\\\"))\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\"^0.6.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"socket2\",\"req\":\"^0.6.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"name\":\"tokio-macros\",\"optional\":true,\"req\":\"~2.6.0\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4.0\"},{\"features\":[\"rt\"],\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.29\",\"target\":\"cfg(tokio_unstable)\"},{\"kind\":\"dev\",\"name\":\"tracing-mock\",\"req\":\"=0.1.0-beta.1\",\"target\":\"cfg(all(tokio_unstable, target_has_atomic = \\\"64\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.0\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"windows-sys\",\"optional\":true,\"req\":\"^0.61\",\"target\":\"cfg(windows)\"},{\"features\":[\"Win32_Foundation\",\"Win32_Security_Authorization\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.61\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[],\"fs\":[],\"full\":[\"fs\",\"io-util\",\"io-std\",\"macros\",\"net\",\"parking_lot\",\"process\",\"rt\",\"rt-multi-thread\",\"signal\",\"sync\",\"time\"],\"io-std\":[],\"io-uring\":[\"dep:io-uring\",\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"dep:slab\"],\"io-util\":[\"bytes\"],\"macros\":[\"tokio-macros\"],\"net\":[\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"mio/net\",\"socket2\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_Security\",\"windows-sys/Win32_Storage_FileSystem\",\"windows-sys/Win32_System_Pipes\",\"windows-sys/Win32_System_SystemServices\"],\"process\":[\"bytes\",\"libc\",\"mio/os-poll\",\"mio/os-ext\",\"mio/net\",\"signal-hook-registry\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_System_Threading\",\"windows-sys/Win32_System_WindowsProgramming\"],\"rt\":[],\"rt-multi-thread\":[\"rt\"],\"signal\":[\"libc\",\"mio/os-poll\",\"mio/net\",\"mio/os-ext\",\"signal-hook-registry\",\"windows-sys/Win32_Foundation\",\"windows-sys/Win32_System_Console\"],\"sync\":[],\"taskdump\":[\"dep:backtrace\"],\"test-util\":[\"rt\",\"sync\",\"time\"],\"time\":[]}}", "toml_0.5.11": "{\"dependencies\":[{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde\",\"req\":\"^1.0.97\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"default\":[],\"preserve_order\":[\"indexmap\"]}}", - "toml_0.9.5": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.15\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.8\"},{\"default_features\":false,\"name\":\"foldhash\",\"optional\":true,\"req\":\"^0.1.5\"},{\"default_features\":false,\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.3.0\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.14.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.145\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.199\"},{\"kind\":\"dev\",\"name\":\"serde-untagged\",\"req\":\"^0.1.7\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.116\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_spanned\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.0\"},{\"kind\":\"dev\",\"name\":\"toml-test-data\",\"req\":\"^2.3.0\"},{\"features\":[\"snapshot\"],\"kind\":\"dev\",\"name\":\"toml-test-harness\",\"req\":\"^1.3.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"toml_datetime\",\"req\":\"^0.7.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"toml_parser\",\"optional\":true,\"req\":\"^1.0.2\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"toml_writer\",\"optional\":true,\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"name\":\"winnow\",\"optional\":true,\"req\":\"^0.7.10\"}],\"features\":{\"debug\":[\"std\",\"toml_parser?/debug\",\"dep:anstream\",\"dep:anstyle\"],\"default\":[\"std\",\"serde\",\"parse\",\"display\"],\"display\":[\"dep:toml_writer\"],\"fast_hash\":[\"preserve_order\",\"dep:foldhash\"],\"parse\":[\"dep:toml_parser\",\"dep:winnow\"],\"preserve_order\":[\"dep:indexmap\",\"std\"],\"serde\":[\"dep:serde\",\"toml_datetime/serde\",\"serde_spanned/serde\"],\"std\":[\"indexmap?/std\",\"serde?/std\",\"toml_parser?/std\",\"toml_writer?/std\",\"toml_datetime/std\",\"serde_spanned/std\"],\"unbounded\":[]}}", + "toml_0.9.11+spec-1.1.0": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.20\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.11\"},{\"default_features\":false,\"name\":\"foldhash\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.11.4\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.14.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.225\"},{\"kind\":\"dev\",\"name\":\"serde-untagged\",\"req\":\"^0.1.9\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.225\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.145\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_spanned\",\"req\":\"^1.0.4\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.21\"},{\"kind\":\"dev\",\"name\":\"toml-test-data\",\"req\":\"^2.3.3\"},{\"features\":[\"snapshot\"],\"kind\":\"dev\",\"name\":\"toml-test-harness\",\"req\":\"^1.3.3\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"toml_datetime\",\"req\":\"^0.7.5\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"toml_parser\",\"optional\":true,\"req\":\"^1.0.6\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"toml_writer\",\"optional\":true,\"req\":\"^1.0.6\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"name\":\"winnow\",\"optional\":true,\"req\":\"^0.7.13\"}],\"features\":{\"debug\":[\"std\",\"toml_parser?/debug\",\"dep:anstream\",\"dep:anstyle\"],\"default\":[\"std\",\"serde\",\"parse\",\"display\"],\"display\":[\"dep:toml_writer\"],\"fast_hash\":[\"preserve_order\",\"dep:foldhash\"],\"parse\":[\"dep:toml_parser\",\"dep:winnow\"],\"preserve_order\":[\"dep:indexmap\",\"std\"],\"serde\":[\"dep:serde_core\",\"toml_datetime/serde\",\"serde_spanned/serde\"],\"std\":[\"indexmap?/std\",\"serde_core?/std\",\"toml_parser?/std\",\"toml_writer?/std\",\"toml_datetime/std\",\"serde_spanned/std\"],\"unbounded\":[]}}", "toml_datetime_0.7.5+spec-1.1.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.225\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.21\"}],\"features\":{\"alloc\":[\"serde_core?/alloc\"],\"default\":[\"std\"],\"serde\":[\"dep:serde_core\"],\"std\":[\"alloc\",\"serde_core?/std\"]}}", "toml_edit_0.23.10+spec-1.0.0": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.20\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.11\"},{\"features\":[\"std\"],\"name\":\"indexmap\",\"req\":\"^2.11.4\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.7.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.225\"},{\"kind\":\"dev\",\"name\":\"serde-untagged\",\"req\":\"^0.1.9\"},{\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.225\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.145\"},{\"features\":[\"serde\"],\"name\":\"serde_spanned\",\"optional\":true,\"req\":\"^1.0.4\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.21\"},{\"kind\":\"dev\",\"name\":\"toml-test-data\",\"req\":\"^2.3.3\"},{\"features\":[\"snapshot\"],\"kind\":\"dev\",\"name\":\"toml-test-harness\",\"req\":\"^1.3.3\"},{\"name\":\"toml_datetime\",\"req\":\"^0.7.4\"},{\"name\":\"toml_parser\",\"optional\":true,\"req\":\"^1.0.5\"},{\"name\":\"toml_writer\",\"optional\":true,\"req\":\"^1.0.5\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.5.0\"},{\"name\":\"winnow\",\"optional\":true,\"req\":\"^0.7.13\"}],\"features\":{\"debug\":[\"toml_parser?/debug\",\"dep:anstream\",\"dep:anstyle\",\"display\"],\"default\":[\"parse\",\"display\"],\"display\":[\"dep:toml_writer\"],\"parse\":[\"dep:toml_parser\",\"dep:winnow\"],\"serde\":[\"dep:serde_core\",\"toml_datetime/serde\",\"dep:serde_spanned\"],\"unbounded\":[]}}", "toml_edit_0.24.0+spec-1.1.0": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.20\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.11\"},{\"features\":[\"std\"],\"name\":\"indexmap\",\"req\":\"^2.11.4\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.7.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.225\"},{\"kind\":\"dev\",\"name\":\"serde-untagged\",\"req\":\"^0.1.9\"},{\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.225\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.145\"},{\"features\":[\"serde\"],\"name\":\"serde_spanned\",\"optional\":true,\"req\":\"^1.0.4\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.21\"},{\"kind\":\"dev\",\"name\":\"toml-test-data\",\"req\":\"^2.3.3\"},{\"features\":[\"snapshot\"],\"kind\":\"dev\",\"name\":\"toml-test-harness\",\"req\":\"^1.3.3\"},{\"name\":\"toml_datetime\",\"req\":\"^0.7.5\"},{\"name\":\"toml_parser\",\"optional\":true,\"req\":\"^1.0.6\"},{\"name\":\"toml_writer\",\"optional\":true,\"req\":\"^1.0.6\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.5.0\"},{\"name\":\"winnow\",\"optional\":true,\"req\":\"^0.7.13\"}],\"features\":{\"debug\":[\"toml_parser?/debug\",\"dep:anstream\",\"dep:anstyle\",\"display\"],\"default\":[\"parse\",\"display\"],\"display\":[\"dep:toml_writer\"],\"parse\":[\"dep:toml_parser\",\"dep:winnow\"],\"serde\":[\"dep:serde_core\",\"toml_datetime/serde\",\"dep:serde_spanned\"],\"unbounded\":[]}}", "toml_parser_1.0.6+spec-1.1.0": "{\"dependencies\":[{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.20\"},{\"features\":[\"test\"],\"kind\":\"dev\",\"name\":\"anstream\",\"req\":\"^0.6.20\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.11\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.21\"},{\"default_features\":false,\"name\":\"winnow\",\"req\":\"^0.7.13\"}],\"features\":{\"alloc\":[],\"debug\":[\"std\",\"dep:anstream\",\"dep:anstyle\"],\"default\":[\"std\"],\"simd\":[\"winnow/simd\"],\"std\":[\"alloc\"],\"unsafe\":[]}}", "toml_writer_1.0.6+spec-1.1.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.7.0\"},{\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.21\"},{\"kind\":\"dev\",\"name\":\"toml_old\",\"package\":\"toml\",\"req\":\"^0.5.11\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", - "tonic-prost_0.14.2": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"http-body\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"name\":\"prost\",\"req\":\"^0.14\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"tonic\",\"req\":\"^0.14.0\"}],\"features\":{}}", - "tonic_0.14.2": "{\"dependencies\":[{\"name\":\"async-trait\",\"optional\":true,\"req\":\"^0.1.13\"},{\"default_features\":false,\"name\":\"axum\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"http\",\"req\":\"^1\"},{\"name\":\"http-body\",\"req\":\"^1\"},{\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"features\":[\"http1\",\"http2\"],\"name\":\"hyper\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"hyper-timeout\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"tokio\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1.4\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1\"},{\"name\":\"pin-project\",\"req\":\"^1.0.11\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.0\"},{\"name\":\"sync_wrapper\",\"req\":\"^1.0.2\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"logging\",\"tls12\"],\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26.1\"},{\"default_features\":false,\"name\":\"tokio-stream\",\"req\":\"^0.1.16\"},{\"default_features\":false,\"name\":\"tower\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"load-shed\",\"timeout\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5\"},{\"name\":\"tower-layer\",\"req\":\"^0.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.13.0\"}],\"features\":{\"_tls-any\":[\"dep:tokio\",\"tokio?/rt\",\"tokio?/macros\",\"tls-connect-info\"],\"channel\":[\"dep:hyper\",\"hyper?/client\",\"dep:hyper-util\",\"hyper-util?/client-legacy\",\"dep:tower\",\"tower?/balance\",\"tower?/buffer\",\"tower?/discover\",\"tower?/limit\",\"tower?/load-shed\",\"tower?/util\",\"dep:tokio\",\"tokio?/time\",\"dep:hyper-timeout\"],\"codegen\":[\"dep:async-trait\"],\"default\":[\"router\",\"transport\",\"codegen\"],\"deflate\":[\"dep:flate2\"],\"gzip\":[\"dep:flate2\"],\"router\":[\"dep:axum\",\"dep:tower\",\"tower?/util\"],\"server\":[\"dep:h2\",\"dep:hyper\",\"hyper?/server\",\"dep:hyper-util\",\"hyper-util?/service\",\"hyper-util?/server-auto\",\"dep:socket2\",\"dep:tokio\",\"tokio?/macros\",\"tokio?/net\",\"tokio?/time\",\"tokio-stream/net\",\"dep:tower\",\"tower?/util\",\"tower?/limit\",\"tower?/load-shed\"],\"tls-aws-lc\":[\"_tls-any\",\"tokio-rustls/aws-lc-rs\"],\"tls-connect-info\":[\"dep:tokio-rustls\"],\"tls-native-roots\":[\"_tls-any\",\"channel\",\"dep:rustls-native-certs\"],\"tls-ring\":[\"_tls-any\",\"tokio-rustls/ring\"],\"tls-webpki-roots\":[\"_tls-any\",\"channel\",\"dep:webpki-roots\"],\"transport\":[\"server\",\"channel\"],\"zstd\":[\"dep:zstd\"]}}", - "tower-http_0.6.6": "{\"dependencies\":[{\"features\":[\"tokio\"],\"name\":\"async-compression\",\"optional\":true,\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22\"},{\"name\":\"bitflags\",\"req\":\"^2.0.2\"},{\"kind\":\"dev\",\"name\":\"brotli\",\"req\":\"^7\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.14\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.14\"},{\"name\":\"http\",\"req\":\"^1.0\"},{\"name\":\"http-body\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"name\":\"http-range-header\",\"optional\":true,\"req\":\"^0.4.0\"},{\"name\":\"httpdate\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"client-legacy\",\"http1\",\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"name\":\"iri-string\",\"optional\":true,\"req\":\"^0.7.0\"},{\"default_features\":false,\"name\":\"mime\",\"optional\":true,\"req\":\"^0.3.17\"},{\"default_features\":false,\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.1.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"sync_wrapper\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.6\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"io\"],\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"tower\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"buffer\",\"util\",\"retry\",\"make\",\"timeout\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5\"},{\"name\":\"tower-layer\",\"req\":\"^0.3.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"v4\"],\"kind\":\"dev\",\"name\":\"uuid\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"zstd\",\"req\":\"^0.13\"}],\"features\":{\"add-extension\":[],\"auth\":[\"base64\",\"validate-request\"],\"catch-panic\":[\"tracing\",\"futures-util/std\",\"dep:http-body\",\"dep:http-body-util\"],\"compression-br\":[\"async-compression/brotli\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"tokio\"],\"compression-deflate\":[\"async-compression/zlib\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"tokio\"],\"compression-full\":[\"compression-br\",\"compression-deflate\",\"compression-gzip\",\"compression-zstd\"],\"compression-gzip\":[\"async-compression/gzip\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"tokio\"],\"compression-zstd\":[\"async-compression/zstd\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"tokio\"],\"cors\":[],\"decompression-br\":[\"async-compression/brotli\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"tokio\"],\"decompression-deflate\":[\"async-compression/zlib\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"tokio\"],\"decompression-full\":[\"decompression-br\",\"decompression-deflate\",\"decompression-gzip\",\"decompression-zstd\"],\"decompression-gzip\":[\"async-compression/gzip\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"tokio\"],\"decompression-zstd\":[\"async-compression/zstd\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"tokio\"],\"default\":[],\"follow-redirect\":[\"futures-util\",\"dep:http-body\",\"iri-string\",\"tower/util\"],\"fs\":[\"futures-core\",\"futures-util\",\"dep:http-body\",\"dep:http-body-util\",\"tokio/fs\",\"tokio-util/io\",\"tokio/io-util\",\"dep:http-range-header\",\"mime_guess\",\"mime\",\"percent-encoding\",\"httpdate\",\"set-status\",\"futures-util/alloc\",\"tracing\"],\"full\":[\"add-extension\",\"auth\",\"catch-panic\",\"compression-full\",\"cors\",\"decompression-full\",\"follow-redirect\",\"fs\",\"limit\",\"map-request-body\",\"map-response-body\",\"metrics\",\"normalize-path\",\"propagate-header\",\"redirect\",\"request-id\",\"sensitive-headers\",\"set-header\",\"set-status\",\"timeout\",\"trace\",\"util\",\"validate-request\"],\"limit\":[\"dep:http-body\",\"dep:http-body-util\"],\"map-request-body\":[],\"map-response-body\":[],\"metrics\":[\"dep:http-body\",\"tokio/time\"],\"normalize-path\":[],\"propagate-header\":[],\"redirect\":[],\"request-id\":[\"uuid\"],\"sensitive-headers\":[],\"set-header\":[],\"set-status\":[],\"timeout\":[\"dep:http-body\",\"tokio/time\"],\"trace\":[\"dep:http-body\",\"tracing\"],\"util\":[\"tower\"],\"validate-request\":[\"mime\"]}}", + "tonic-prost_0.14.3": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"http-body\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"name\":\"prost\",\"req\":\"^0.14\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"tonic\",\"req\":\"^0.14.0\"}],\"features\":{}}", + "tonic_0.14.3": "{\"dependencies\":[{\"name\":\"async-trait\",\"optional\":true,\"req\":\"^0.1.13\"},{\"default_features\":false,\"name\":\"axum\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"bytes\",\"req\":\"^1.0\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"h2\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"http\",\"req\":\"^1.1.0\"},{\"name\":\"http-body\",\"req\":\"^1\"},{\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"features\":[\"http1\",\"http2\"],\"name\":\"hyper\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"hyper-timeout\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"tokio\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1.11\"},{\"name\":\"percent-encoding\",\"req\":\"^2.1\"},{\"name\":\"pin-project\",\"req\":\"^1.0.11\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0\"},{\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"^0.8\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"optional\":true,\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.0\"},{\"name\":\"sync_wrapper\",\"req\":\"^1.0.2\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"logging\",\"tls12\"],\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"^0.26.1\"},{\"default_features\":false,\"name\":\"tokio-stream\",\"req\":\"^0.1.16\"},{\"default_features\":false,\"name\":\"tower\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"load-shed\",\"timeout\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5\"},{\"name\":\"tower-layer\",\"req\":\"^0.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.13.0\"}],\"features\":{\"_tls-any\":[\"dep:tokio\",\"tokio?/rt\",\"tokio?/macros\",\"tls-connect-info\"],\"channel\":[\"dep:hyper\",\"hyper?/client\",\"dep:hyper-util\",\"hyper-util?/client-legacy\",\"dep:tower\",\"tower?/balance\",\"tower?/buffer\",\"tower?/discover\",\"tower?/limit\",\"tower?/load-shed\",\"tower?/util\",\"dep:tokio\",\"tokio?/time\",\"dep:hyper-timeout\"],\"codegen\":[\"dep:async-trait\"],\"default\":[\"router\",\"transport\",\"codegen\"],\"deflate\":[\"dep:flate2\"],\"gzip\":[\"dep:flate2\"],\"router\":[\"dep:axum\",\"dep:tower\",\"tower?/util\"],\"server\":[\"dep:h2\",\"dep:hyper\",\"hyper?/server\",\"dep:hyper-util\",\"hyper-util?/service\",\"hyper-util?/server-auto\",\"dep:socket2\",\"dep:tokio\",\"tokio?/macros\",\"tokio?/net\",\"tokio?/time\",\"tokio-stream/net\",\"dep:tower\",\"tower?/util\",\"tower?/limit\",\"tower?/load-shed\"],\"tls-aws-lc\":[\"_tls-any\",\"tokio-rustls/aws-lc-rs\"],\"tls-connect-info\":[\"dep:tokio-rustls\"],\"tls-native-roots\":[\"_tls-any\",\"channel\",\"dep:rustls-native-certs\"],\"tls-ring\":[\"_tls-any\",\"tokio-rustls/ring\"],\"tls-webpki-roots\":[\"_tls-any\",\"channel\",\"dep:webpki-roots\"],\"transport\":[\"server\",\"channel\"],\"zstd\":[\"dep:zstd\"]}}", + "tower-http_0.6.8": "{\"dependencies\":[{\"features\":[\"tokio\"],\"name\":\"async-compression\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22\"},{\"name\":\"bitflags\",\"req\":\"^2.0.2\"},{\"kind\":\"dev\",\"name\":\"brotli\",\"req\":\"^8\"},{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.14\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.14\"},{\"name\":\"http\",\"req\":\"^1.0\"},{\"name\":\"http-body\",\"optional\":true,\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"name\":\"http-range-header\",\"optional\":true,\"req\":\"^0.4.0\"},{\"name\":\"httpdate\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"client-legacy\",\"http1\",\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"name\":\"iri-string\",\"optional\":true,\"req\":\"^0.7.0\"},{\"default_features\":false,\"name\":\"mime\",\"optional\":true,\"req\":\"^0.3.17\"},{\"default_features\":false,\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.1.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"sync_wrapper\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.6\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"io\"],\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"tower\",\"optional\":true,\"req\":\"^0.5\"},{\"features\":[\"buffer\",\"util\",\"retry\",\"make\",\"timeout\"],\"kind\":\"dev\",\"name\":\"tower\",\"req\":\"^0.5\"},{\"name\":\"tower-layer\",\"req\":\"^0.3.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"zstd\",\"req\":\"^0.13\"}],\"features\":{\"add-extension\":[],\"auth\":[\"base64\",\"validate-request\"],\"catch-panic\":[\"tracing\",\"futures-util/std\",\"dep:http-body\",\"dep:http-body-util\"],\"compression-br\":[\"async-compression/brotli\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"tokio\"],\"compression-deflate\":[\"async-compression/zlib\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"tokio\"],\"compression-full\":[\"compression-br\",\"compression-deflate\",\"compression-gzip\",\"compression-zstd\"],\"compression-gzip\":[\"async-compression/gzip\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"tokio\"],\"compression-zstd\":[\"async-compression/zstd\",\"futures-core\",\"dep:http-body\",\"tokio-util\",\"tokio\"],\"cors\":[],\"decompression-br\":[\"async-compression/brotli\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"tokio\"],\"decompression-deflate\":[\"async-compression/zlib\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"tokio\"],\"decompression-full\":[\"decompression-br\",\"decompression-deflate\",\"decompression-gzip\",\"decompression-zstd\"],\"decompression-gzip\":[\"async-compression/gzip\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"tokio\"],\"decompression-zstd\":[\"async-compression/zstd\",\"futures-core\",\"dep:http-body\",\"dep:http-body-util\",\"tokio-util\",\"tokio\"],\"default\":[],\"follow-redirect\":[\"futures-util\",\"dep:http-body\",\"iri-string\",\"tower/util\"],\"fs\":[\"futures-core\",\"futures-util\",\"dep:http-body\",\"dep:http-body-util\",\"tokio/fs\",\"tokio-util/io\",\"tokio/io-util\",\"dep:http-range-header\",\"mime_guess\",\"mime\",\"percent-encoding\",\"httpdate\",\"set-status\",\"futures-util/alloc\",\"tracing\"],\"full\":[\"add-extension\",\"auth\",\"catch-panic\",\"compression-full\",\"cors\",\"decompression-full\",\"follow-redirect\",\"fs\",\"limit\",\"map-request-body\",\"map-response-body\",\"metrics\",\"normalize-path\",\"propagate-header\",\"redirect\",\"request-id\",\"sensitive-headers\",\"set-header\",\"set-status\",\"timeout\",\"trace\",\"util\",\"validate-request\"],\"limit\":[\"dep:http-body\",\"dep:http-body-util\"],\"map-request-body\":[],\"map-response-body\":[],\"metrics\":[\"dep:http-body\",\"tokio/time\"],\"normalize-path\":[],\"propagate-header\":[],\"redirect\":[],\"request-id\":[\"uuid\"],\"sensitive-headers\":[],\"set-header\":[],\"set-status\":[],\"timeout\":[\"dep:http-body\",\"tokio/time\"],\"trace\":[\"dep:http-body\",\"tracing\"],\"util\":[\"tower\"],\"validate-request\":[\"mime\"]}}", "tower-layer_0.3.3": "{\"dependencies\":[],\"features\":{}}", "tower-service_0.3.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.22\"},{\"kind\":\"dev\",\"name\":\"http\",\"req\":\"^0.2\"},{\"features\":[\"macros\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.6.2\"},{\"kind\":\"dev\",\"name\":\"tower-layer\",\"req\":\"^0.3\"}],\"features\":{}}", - "tower_0.5.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.22\"},{\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3.22\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.22\"},{\"default_features\":false,\"name\":\"hdrhistogram\",\"optional\":true,\"req\":\"^7.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"hdrhistogram\",\"req\":\"^7.0\"},{\"kind\":\"dev\",\"name\":\"http\",\"req\":\"^1\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.0.2\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.4.0\"},{\"name\":\"pin-project-lite\",\"optional\":true,\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"sync_wrapper\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.6.2\"},{\"features\":[\"macros\",\"sync\",\"test-util\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.6.2\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7.0\"},{\"name\":\"tower-layer\",\"req\":\"^0.3.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3.3\"},{\"kind\":\"dev\",\"name\":\"tower-test\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.2\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.2\"},{\"default_features\":false,\"features\":[\"fmt\",\"ansi\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"}],\"features\":{\"__common\":[\"futures-core\",\"pin-project-lite\"],\"balance\":[\"discover\",\"load\",\"ready-cache\",\"make\",\"slab\",\"util\"],\"buffer\":[\"__common\",\"tokio/sync\",\"tokio/rt\",\"tokio-util\",\"tracing\"],\"discover\":[\"__common\"],\"filter\":[\"__common\",\"futures-util\"],\"full\":[\"balance\",\"buffer\",\"discover\",\"filter\",\"hedge\",\"limit\",\"load\",\"load-shed\",\"make\",\"ready-cache\",\"reconnect\",\"retry\",\"spawn-ready\",\"steer\",\"timeout\",\"util\"],\"hedge\":[\"util\",\"filter\",\"futures-util\",\"hdrhistogram\",\"tokio/time\",\"tracing\"],\"limit\":[\"__common\",\"tokio/time\",\"tokio/sync\",\"tokio-util\",\"tracing\"],\"load\":[\"__common\",\"tokio/time\",\"tracing\"],\"load-shed\":[\"__common\"],\"log\":[\"tracing/log\"],\"make\":[\"futures-util\",\"pin-project-lite\",\"tokio/io-std\"],\"ready-cache\":[\"futures-core\",\"futures-util\",\"indexmap\",\"tokio/sync\",\"tracing\",\"pin-project-lite\"],\"reconnect\":[\"make\",\"tokio/io-std\",\"tracing\"],\"retry\":[\"__common\",\"tokio/time\",\"util\"],\"spawn-ready\":[\"__common\",\"futures-util\",\"tokio/sync\",\"tokio/rt\",\"util\",\"tracing\"],\"steer\":[],\"timeout\":[\"pin-project-lite\",\"tokio/time\"],\"util\":[\"__common\",\"futures-util\",\"pin-project-lite\",\"sync_wrapper\"]}}", - "tracing-appender_0.2.3": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.6\"},{\"name\":\"crossbeam-channel\",\"req\":\"^0.5.6\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12.1\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"},{\"name\":\"thiserror\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"formatting\",\"parsing\"],\"name\":\"time\",\"req\":\"^0.3.2\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"default_features\":false,\"features\":[\"fmt\",\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3.18\"}],\"features\":{}}", + "tower_0.5.3": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.22\"},{\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3.22\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.22\"},{\"default_features\":false,\"features\":[\"async-await-macro\"],\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.22\"},{\"default_features\":false,\"name\":\"hdrhistogram\",\"optional\":true,\"req\":\"^7.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"hdrhistogram\",\"req\":\"^7.0\"},{\"kind\":\"dev\",\"name\":\"http\",\"req\":\"^1\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.0.2\"},{\"name\":\"pin-project-lite\",\"optional\":true,\"req\":\"^0.2.7\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"name\":\"slab\",\"optional\":true,\"req\":\"^0.4.9\"},{\"name\":\"sync_wrapper\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.6.2\"},{\"features\":[\"macros\",\"sync\",\"test-util\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.6.2\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1.1\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7.0\"},{\"name\":\"tower-layer\",\"req\":\"^0.3.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3.3\"},{\"kind\":\"dev\",\"name\":\"tower-test\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.2\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.2\"},{\"default_features\":false,\"features\":[\"fmt\",\"ansi\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"}],\"features\":{\"balance\":[\"discover\",\"load\",\"ready-cache\",\"make\",\"slab\",\"util\"],\"buffer\":[\"tokio/sync\",\"tokio/rt\",\"tokio-util\",\"tracing\",\"pin-project-lite\"],\"discover\":[\"futures-core\",\"pin-project-lite\"],\"filter\":[\"futures-util\",\"pin-project-lite\"],\"full\":[\"balance\",\"buffer\",\"discover\",\"filter\",\"hedge\",\"limit\",\"load\",\"load-shed\",\"make\",\"ready-cache\",\"reconnect\",\"retry\",\"spawn-ready\",\"steer\",\"timeout\",\"util\"],\"hedge\":[\"util\",\"filter\",\"futures-util\",\"hdrhistogram\",\"tokio/time\",\"tracing\"],\"limit\":[\"tokio/time\",\"tokio/sync\",\"tokio-util\",\"tracing\",\"pin-project-lite\"],\"load\":[\"tokio/time\",\"tracing\",\"pin-project-lite\"],\"load-shed\":[\"pin-project-lite\"],\"log\":[\"tracing/log\"],\"make\":[\"pin-project-lite\",\"tokio\"],\"ready-cache\":[\"futures-core\",\"futures-util\",\"indexmap\",\"tokio/sync\",\"tracing\",\"pin-project-lite\"],\"reconnect\":[\"make\",\"tracing\"],\"retry\":[\"tokio/time\",\"util\"],\"spawn-ready\":[\"futures-util\",\"tokio/sync\",\"tokio/rt\",\"util\",\"tracing\"],\"steer\":[],\"timeout\":[\"pin-project-lite\",\"tokio/time\"],\"tokio-stream\":[],\"util\":[\"futures-core\",\"futures-util\",\"pin-project-lite\",\"sync_wrapper\"]}}", + "tracing-appender_0.2.4": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.6\"},{\"name\":\"crossbeam-channel\",\"req\":\"^0.5.6\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12.1\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"formatting\",\"parsing\"],\"name\":\"time\",\"req\":\"^0.3.2\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"default_features\":false,\"features\":[\"fmt\",\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3.18\"}],\"features\":{}}", "tracing-attributes_0.1.31": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1.67\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"name\":\"quote\",\"req\":\"^1.0.20\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.9\"},{\"default_features\":false,\"features\":[\"full\",\"parsing\",\"printing\",\"visit-mut\",\"clone-impls\",\"extra-traits\",\"proc-macro\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"tokio-test\",\"req\":\"^0.4.2\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"features\":[\"env-filter\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.64\"}],\"features\":{\"async-await\":[]}}", "tracing-core_0.1.36": "{\"dependencies\":[{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.13.0\"},{\"default_features\":false,\"name\":\"valuable\",\"optional\":true,\"req\":\"^0.1.0\",\"target\":\"cfg(tracing_unstable)\"}],\"features\":{\"default\":[\"std\",\"valuable?/std\"],\"std\":[\"once_cell\"]}}", "tracing-error_0.2.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"default_features\":false,\"features\":[\"registry\",\"fmt\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"}],\"features\":{\"default\":[\"traced-error\"],\"traced-error\":[]}}", "tracing-log_0.2.0": "{\"dependencies\":[{\"name\":\"ahash\",\"optional\":true,\"req\":\"^0.7.6\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.6\"},{\"name\":\"log\",\"req\":\"^0.4.17\"},{\"name\":\"lru\",\"optional\":true,\"req\":\"^0.7.7\"},{\"name\":\"once_cell\",\"req\":\"^1.13.0\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"name\":\"tracing-core\",\"req\":\"^0.1.28\"}],\"features\":{\"default\":[\"log-tracer\",\"std\"],\"interest-cache\":[\"lru\",\"ahash\"],\"log-tracer\":[],\"std\":[\"log/std\"]}}", - "tracing-opentelemetry_0.32.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1.56\"},{\"default_features\":false,\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.17\"},{\"name\":\"js-sys\",\"req\":\"^0.3.64\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"lazy_static\",\"optional\":true,\"req\":\"^1.0.2\"},{\"default_features\":false,\"features\":[\"trace\"],\"name\":\"opentelemetry\",\"req\":\"^0.31.0\"},{\"features\":[\"trace\",\"metrics\"],\"kind\":\"dev\",\"name\":\"opentelemetry\",\"req\":\"^0.31.0\"},{\"features\":[\"metrics\",\"grpc-tonic\"],\"kind\":\"dev\",\"name\":\"opentelemetry-otlp\",\"req\":\"^0.31.0\"},{\"features\":[\"semconv_experimental\"],\"kind\":\"dev\",\"name\":\"opentelemetry-semantic-conventions\",\"req\":\"^0.31.0\"},{\"features\":[\"trace\",\"metrics\"],\"kind\":\"dev\",\"name\":\"opentelemetry-stdout\",\"req\":\"^0.31.0\"},{\"default_features\":false,\"features\":[\"trace\"],\"name\":\"opentelemetry_sdk\",\"req\":\"^0.31.0\"},{\"default_features\":false,\"features\":[\"trace\",\"rt-tokio\",\"experimental_metrics_custom_reader\",\"testing\"],\"kind\":\"dev\",\"name\":\"opentelemetry_sdk\",\"req\":\"^0.31.0\"},{\"features\":[\"flamegraph\",\"criterion\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.15.0\",\"target\":\"cfg(not(target_os = \\\"windows\\\"))\"},{\"name\":\"rustversion\",\"req\":\"^1.0.9\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tokio-stream\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"default_features\":false,\"features\":[\"std\",\"attributes\"],\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"name\":\"tracing-core\",\"req\":\"^0.1.28\"},{\"kind\":\"dev\",\"name\":\"tracing-error\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"tracing-log\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"registry\",\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"default_features\":false,\"features\":[\"registry\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"name\":\"web-time\",\"req\":\"^1.0.0\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"}],\"features\":{\"default\":[\"tracing-log\",\"metrics\"],\"metrics\":[\"opentelemetry/metrics\",\"opentelemetry_sdk/metrics\",\"smallvec\"]}}", + "tracing-opentelemetry_0.32.1": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"name\":\"js-sys\",\"req\":\"^0.3.64\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"},{\"name\":\"lazy_static\",\"optional\":true,\"req\":\"^1.0.2\"},{\"default_features\":false,\"features\":[\"trace\"],\"name\":\"opentelemetry\",\"req\":\"^0.31.0\"},{\"features\":[\"trace\",\"metrics\"],\"kind\":\"dev\",\"name\":\"opentelemetry\",\"req\":\"^0.31.0\"},{\"features\":[\"metrics\",\"grpc-tonic\"],\"kind\":\"dev\",\"name\":\"opentelemetry-otlp\",\"req\":\"^0.31.0\"},{\"features\":[\"semconv_experimental\"],\"kind\":\"dev\",\"name\":\"opentelemetry-semantic-conventions\",\"req\":\"^0.31.0\"},{\"features\":[\"trace\",\"metrics\"],\"kind\":\"dev\",\"name\":\"opentelemetry-stdout\",\"req\":\"^0.31.0\"},{\"default_features\":false,\"features\":[\"trace\",\"experimental_metrics_custom_reader\",\"testing\"],\"kind\":\"dev\",\"name\":\"opentelemetry_sdk\",\"req\":\"^0.31.0\"},{\"features\":[\"flamegraph\",\"criterion\"],\"kind\":\"dev\",\"name\":\"pprof\",\"req\":\"^0.15.0\",\"target\":\"cfg(not(target_os = \\\"windows\\\"))\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"default_features\":false,\"features\":[\"std\",\"attributes\"],\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.35\"},{\"name\":\"tracing-core\",\"req\":\"^0.1.28\"},{\"kind\":\"dev\",\"name\":\"tracing-error\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"tracing-log\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"registry\",\"std\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3.22\"},{\"default_features\":false,\"features\":[\"registry\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.0\"},{\"name\":\"web-time\",\"req\":\"^1.0.0\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\")))\"}],\"features\":{\"default\":[\"tracing-log\",\"metrics\"],\"metrics\":[\"opentelemetry/metrics\",\"smallvec\"]}}", "tracing-subscriber_0.3.22": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"clock\",\"std\"],\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.26\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.17\"},{\"name\":\"matchers\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"nu-ansi-term\",\"optional\":true,\"req\":\"^0.50.0\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.13.0\"},{\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12.1\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"regex-automata\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.140\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.82\"},{\"name\":\"sharded-slab\",\"optional\":true,\"req\":\"^0.1.4\"},{\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1.9.0\"},{\"name\":\"thread_local\",\"optional\":true,\"req\":\"^1.1.4\"},{\"features\":[\"formatting\"],\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.2\"},{\"features\":[\"formatting\",\"macros\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.2\"},{\"features\":[\"rt\",\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.43\"},{\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1.43\"},{\"default_features\":false,\"name\":\"tracing-core\",\"req\":\"^0.1.35\"},{\"default_features\":false,\"features\":[\"std-future\",\"std\"],\"kind\":\"dev\",\"name\":\"tracing-futures\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"log-tracer\",\"std\"],\"name\":\"tracing-log\",\"optional\":true,\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"tracing-log\",\"req\":\"^0.2.0\"},{\"name\":\"tracing-serde\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"valuable-serde\",\"optional\":true,\"req\":\"^0.1.0\",\"target\":\"cfg(tracing_unstable)\"},{\"default_features\":false,\"name\":\"valuable_crate\",\"optional\":true,\"package\":\"valuable\",\"req\":\"^0.1.0\",\"target\":\"cfg(tracing_unstable)\"}],\"features\":{\"alloc\":[],\"ansi\":[\"fmt\",\"nu-ansi-term\"],\"default\":[\"smallvec\",\"fmt\",\"ansi\",\"tracing-log\",\"std\"],\"env-filter\":[\"matchers\",\"once_cell\",\"tracing\",\"std\",\"thread_local\",\"dep:regex-automata\"],\"fmt\":[\"registry\",\"std\"],\"json\":[\"tracing-serde\",\"serde\",\"serde_json\"],\"local-time\":[\"time/local-offset\"],\"nu-ansi-term\":[\"dep:nu-ansi-term\"],\"regex\":[],\"registry\":[\"sharded-slab\",\"thread_local\",\"std\"],\"std\":[\"alloc\",\"tracing-core/std\"],\"valuable\":[\"tracing-core/valuable\",\"valuable_crate\",\"valuable-serde\",\"tracing-serde/valuable\"]}}", "tracing-test-macro_0.2.5": "{\"dependencies\":[{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"no-env-filter\":[]}}", "tracing-test_0.2.5": "{\"dependencies\":[{\"features\":[\"rt-multi-thread\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"tracing\",\"req\":\"^0.1\"},{\"name\":\"tracing-core\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\"],\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"tracing-test-macro\",\"req\":\"^0.2.5\"}],\"features\":{\"no-env-filter\":[\"tracing-test-macro/no-env-filter\"]}}", "tracing_0.1.44": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.6\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.21\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.17\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4.17\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.9\"},{\"name\":\"tracing-attributes\",\"optional\":true,\"req\":\"^0.1.31\"},{\"default_features\":false,\"name\":\"tracing-core\",\"req\":\"^0.1.36\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.38\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"}],\"features\":{\"async-await\":[],\"attributes\":[\"tracing-attributes\"],\"default\":[\"std\",\"attributes\"],\"log-always\":[\"log\"],\"max_level_debug\":[],\"max_level_error\":[],\"max_level_info\":[],\"max_level_off\":[],\"max_level_trace\":[],\"max_level_warn\":[],\"release_max_level_debug\":[],\"release_max_level_error\":[],\"release_max_level_info\":[],\"release_max_level_off\":[],\"release_max_level_trace\":[],\"release_max_level_warn\":[],\"std\":[\"tracing-core/std\"],\"valuable\":[\"tracing-core/valuable\"]}}", - "tree-sitter-bash_0.25.0": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.1\"},{\"kind\":\"dev\",\"name\":\"tree-sitter\",\"req\":\"^0.25\"},{\"name\":\"tree-sitter-language\",\"req\":\"^0.1\"}],\"features\":{}}", + "tree-sitter-bash_0.25.1": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.1\"},{\"kind\":\"dev\",\"name\":\"tree-sitter\",\"req\":\"^0.25\"},{\"name\":\"tree-sitter-language\",\"req\":\"^0.1\"}],\"features\":{}}", "tree-sitter-highlight_0.25.10": "{\"dependencies\":[{\"name\":\"regex\",\"req\":\"^1.11.1\"},{\"name\":\"streaming-iterator\",\"req\":\"^0.1.9\"},{\"name\":\"thiserror\",\"req\":\"^2.0.11\"},{\"name\":\"tree-sitter\",\"req\":\"^0.25.10\"}],\"features\":{}}", - "tree-sitter-language_0.1.5": "{\"dependencies\":[],\"features\":{}}", + "tree-sitter-language_0.1.7": "{\"dependencies\":[],\"features\":{}}", "tree-sitter_0.25.10": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"bindgen\",\"optional\":true,\"req\":\"^0.71.1\"},{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.2.10\"},{\"default_features\":false,\"features\":[\"unicode\"],\"name\":\"regex\",\"req\":\"^1.11.1\"},{\"default_features\":false,\"name\":\"regex-syntax\",\"req\":\"^0.8.5\"},{\"features\":[\"preserve_order\"],\"kind\":\"build\",\"name\":\"serde_json\",\"req\":\"^1.0.137\"},{\"name\":\"streaming-iterator\",\"req\":\"^0.1.9\"},{\"name\":\"tree-sitter-language\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"cranelift\",\"gc-drc\"],\"name\":\"wasmtime-c-api\",\"optional\":true,\"package\":\"wasmtime-c-api-impl\",\"req\":\"^29.0.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"regex/std\",\"regex/perf\",\"regex-syntax/unicode\"],\"wasm\":[\"std\",\"wasmtime-c-api\"]}}", - "tree_magic_mini_3.2.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.0\"},{\"name\":\"memchr\",\"req\":\"^2.0\"},{\"name\":\"nom\",\"req\":\"^7.0\"},{\"name\":\"once_cell\",\"req\":\"^1.0\"},{\"name\":\"petgraph\",\"req\":\"^0.6.0\"},{\"name\":\"tree_magic_db\",\"optional\":true,\"req\":\"^3.0\"}],\"features\":{\"with-gpl-data\":[\"dep:tree_magic_db\"]}}", + "tree_magic_mini_3.2.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.0\"},{\"name\":\"memchr\",\"req\":\"^2.0\"},{\"name\":\"nom\",\"req\":\"^8.0\"},{\"default_features\":false,\"name\":\"petgraph\",\"req\":\"^0.8.0\"},{\"name\":\"tree_magic_db\",\"optional\":true,\"req\":\"^3.0\"}],\"features\":{\"with-gpl-data\":[\"dep:tree_magic_db\"]}}", "try-lock_0.2.5": "{\"dependencies\":[],\"features\":{}}", "ts-rs-macros_11.1.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.28\"},{\"name\":\"termcolor\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"no-serde-warnings\":[],\"serde-compat\":[\"termcolor\"]}}", "ts-rs_11.1.0": "{\"dependencies\":[{\"features\":[\"serde\"],\"name\":\"bigdecimal\",\"optional\":true,\"req\":\">=0.0.13, <0.5\"},{\"name\":\"bson\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4\"},{\"name\":\"dprint-plugin-typescript\",\"optional\":true,\"req\":\"=0.95\"},{\"name\":\"heapless\",\"optional\":true,\"req\":\">=0.7, <0.9\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"ordered-float\",\"optional\":true,\"req\":\">=3, <6\"},{\"name\":\"semver\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"name\":\"smol_str\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"sync\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.40\"},{\"name\":\"ts-rs-macros\",\"req\":\"=11.1.0\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"bigdecimal-impl\":[\"bigdecimal\"],\"bson-uuid-impl\":[\"bson\"],\"bytes-impl\":[\"bytes\"],\"chrono-impl\":[\"chrono\"],\"default\":[\"serde-compat\"],\"format\":[\"dprint-plugin-typescript\"],\"heapless-impl\":[\"heapless\"],\"import-esm\":[],\"indexmap-impl\":[\"indexmap\"],\"no-serde-warnings\":[\"ts-rs-macros/no-serde-warnings\"],\"ordered-float-impl\":[\"ordered-float\"],\"semver-impl\":[\"semver\"],\"serde-compat\":[\"ts-rs-macros/serde-compat\"],\"serde-json-impl\":[\"serde_json\"],\"smol_str-impl\":[\"smol_str\"],\"tokio-impl\":[\"tokio\"],\"url-impl\":[\"url\"],\"uuid-impl\":[\"uuid\"]}}", - "typenum_1.18.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"scale-info\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"const-generics\":[],\"force_unix_path_separator\":[],\"i128\":[],\"no_std\":[],\"scale_info\":[\"scale-info/derive\"],\"strict\":[]}}", + "type-map_0.5.1": "{\"dependencies\":[{\"name\":\"rustc-hash\",\"req\":\"^2\"}],\"features\":{}}", + "typenum_1.19.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"scale-info\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"const-generics\":[],\"force_unix_path_separator\":[],\"i128\":[],\"no_std\":[],\"scale_info\":[\"scale-info/derive\"],\"strict\":[]}}", "uds_windows_1.1.0": "{\"dependencies\":[{\"name\":\"memoffset\",\"req\":\"^0.9.0\"},{\"name\":\"tempfile\",\"req\":\"^3\",\"target\":\"cfg(windows)\"},{\"features\":[\"winsock2\",\"ws2def\",\"minwinbase\",\"ntdef\",\"processthreadsapi\",\"handleapi\",\"ws2tcpip\",\"winbase\"],\"name\":\"winapi\",\"req\":\"^0.3.9\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "uname_0.1.1": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2\"}],\"features\":{}}", "unarray_0.1.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"test-strategy\",\"req\":\"^0.2\"}],\"features\":{}}", - "unicase_2.8.1": "{\"dependencies\":[],\"features\":{\"nightly\":[]}}", + "unic-langid-impl_0.9.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"tinystr\",\"req\":\"^0.8.0\"}],\"features\":{\"binary\":[\"serde\",\"serde_json\"],\"likelysubtags\":[]}}", + "unic-langid_0.9.6": "{\"dependencies\":[{\"name\":\"unic-langid-impl\",\"req\":\"^0.9.6\"},{\"name\":\"unic-langid-macros\",\"optional\":true,\"req\":\"^0.9.6\"},{\"kind\":\"dev\",\"name\":\"unic-langid-macros\",\"req\":\"^0.9.6\"}],\"features\":{\"default\":[],\"likelysubtags\":[\"unic-langid-impl/likelysubtags\"],\"macros\":[\"unic-langid-macros\"],\"serde\":[\"unic-langid-impl/serde\"]}}", + "unicase_2.9.0": "{\"dependencies\":[],\"features\":{\"nightly\":[]}}", "unicode-bidi_0.3.18": "{\"dependencies\":[{\"name\":\"flame\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"flamer\",\"optional\":true,\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\">=0.8, <2.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\">=0.8, <2.0\"},{\"features\":[\"union\"],\"name\":\"smallvec\",\"optional\":true,\"req\":\">=1.13\"}],\"features\":{\"bench_it\":[],\"default\":[\"std\",\"hardcoded-data\"],\"flame_it\":[\"flame\",\"flamer\"],\"hardcoded-data\":[],\"std\":[],\"unstable\":[],\"with_serde\":[\"serde\"]}}", - "unicode-ident_1.0.18": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"fst\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"roaring\",\"req\":\"^0.10\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"ucd-trie\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"unicode-xid\",\"req\":\"^0.2.6\"}],\"features\":{}}", + "unicode-ident_1.0.22": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"fst\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"roaring\",\"req\":\"^0.11\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"ucd-trie\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"unicode-xid\",\"req\":\"^0.2.6\"}],\"features\":{}}", "unicode-linebreak_0.1.5": "{\"dependencies\":[],\"features\":{}}", "unicode-normalization_0.1.25": "{\"dependencies\":[{\"features\":[\"alloc\"],\"name\":\"tinyvec\",\"req\":\"^1\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "unicode-properties_0.1.4": "{\"dependencies\":[],\"features\":{\"default\":[\"general-category\",\"emoji\"],\"emoji\":[],\"general-category\":[]}}", @@ -1264,16 +1320,17 @@ "unicode-width_0.1.14": "{\"dependencies\":[{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"std\",\"optional\":true,\"package\":\"rustc-std-workspace-std\",\"req\":\"^1.0\"}],\"features\":{\"cjk\":[],\"default\":[\"cjk\"],\"no_std\":[],\"rustc-dep-of-std\":[\"std\",\"core\",\"compiler_builtins\"]}}", "unicode-width_0.2.1": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"std\",\"optional\":true,\"package\":\"rustc-std-workspace-std\",\"req\":\"^1.0\"}],\"features\":{\"cjk\":[],\"default\":[\"cjk\"],\"no_std\":[],\"rustc-dep-of-std\":[\"std\",\"core\"]}}", "unicode-xid_0.2.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"}],\"features\":{\"bench\":[],\"default\":[],\"no_std\":[]}}", + "universal-hash_0.5.1": "{\"dependencies\":[{\"name\":\"crypto-common\",\"req\":\"^0.1.6\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2.4\"}],\"features\":{\"std\":[\"crypto-common/std\"]}}", "unsafe-libyaml_0.2.11": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1.0\"}],\"features\":{}}", "untrusted_0.9.0": "{\"dependencies\":[],\"features\":{}}", "ureq-proto_0.5.3": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"base64\",\"req\":\"^0.22.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"http\",\"req\":\"^1.1.0\"},{\"default_features\":false,\"name\":\"httparse\",\"req\":\"^1.8.0\"},{\"name\":\"log\",\"req\":\"^0.4.22\"}],\"features\":{\"client\":[],\"default\":[\"client\",\"server\"],\"server\":[]}}", "ureq_3.1.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"assert_no_alloc\",\"req\":\"^1.1.2\"},{\"kind\":\"dev\",\"name\":\"auto-args\",\"req\":\"^0.3.0\"},{\"name\":\"base64\",\"req\":\"^0.22.1\"},{\"name\":\"brotli-decompressor\",\"optional\":true,\"req\":\"^5.0.0\"},{\"default_features\":false,\"features\":[\"preserve_order\"],\"name\":\"cookie_store\",\"optional\":true,\"req\":\"^0.22\"},{\"default_features\":false,\"features\":[\"pem\",\"std\"],\"name\":\"der\",\"optional\":true,\"req\":\"^0.7.9\"},{\"name\":\"encoding_rs\",\"optional\":true,\"req\":\"^0.8.34\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.7\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0.30\"},{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.2.15\"},{\"name\":\"log\",\"req\":\"^0.4.25\"},{\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0.5\"},{\"default_features\":false,\"name\":\"native-tls\",\"optional\":true,\"req\":\"^0.2.12\"},{\"name\":\"percent-encoding\",\"req\":\"^2.3.1\"},{\"default_features\":false,\"features\":[\"logging\",\"std\",\"tls12\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"^0.23.22\"},{\"features\":[\"aws-lc-rs\"],\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls-pki-types\",\"optional\":true,\"req\":\"^1.11.0\"},{\"default_features\":false,\"name\":\"rustls-platform-verifier\",\"optional\":true,\"req\":\"^0.6.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.138\"},{\"features\":[\"std\",\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.204\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.120\"},{\"name\":\"socks\",\"optional\":true,\"req\":\"^0.3.4\"},{\"default_features\":false,\"features\":[\"client\"],\"name\":\"ureq-proto\",\"req\":\"^0.5.2\"},{\"default_features\":false,\"name\":\"url\",\"optional\":true,\"req\":\"^2.3.1\"},{\"name\":\"utf-8\",\"req\":\"^0.7.6\"},{\"default_features\":false,\"name\":\"webpki-root-certs\",\"optional\":true,\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"^1.0.0\"}],\"features\":{\"_doc\":[\"rustls?/aws-lc-rs\"],\"_ring\":[\"rustls?/ring\"],\"_rustls\":[],\"_test\":[],\"_tls\":[\"dep:rustls-pki-types\"],\"_url\":[\"dep:url\"],\"brotli\":[\"dep:brotli-decompressor\"],\"charset\":[\"dep:encoding_rs\"],\"cookies\":[\"dep:cookie_store\",\"_url\"],\"default\":[\"rustls\",\"gzip\"],\"gzip\":[\"dep:flate2\"],\"json\":[\"dep:serde\",\"dep:serde_json\",\"cookie_store?/serde_json\"],\"multipart\":[\"dep:mime_guess\",\"dep:getrandom\"],\"native-tls\":[\"dep:native-tls\",\"dep:der\",\"_tls\",\"dep:webpki-root-certs\"],\"platform-verifier\":[\"dep:rustls-platform-verifier\"],\"rustls\":[\"rustls-no-provider\",\"_ring\"],\"rustls-no-provider\":[\"dep:rustls\",\"_tls\",\"dep:webpki-roots\",\"_rustls\"],\"socks-proxy\":[\"dep:socks\"],\"vendored\":[\"native-tls?/vendored\"]}}", - "url_2.5.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"form_urlencoded\",\"req\":\"^1.2.1\"},{\"default_features\":false,\"features\":[\"alloc\",\"compiled_data\"],\"name\":\"idna\",\"req\":\"^1.0.3\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"percent-encoding\",\"req\":\"^2.3.1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"}],\"features\":{\"debugger_visualizer\":[],\"default\":[\"std\"],\"expose_internals\":[],\"std\":[\"idna/std\",\"percent-encoding/std\",\"form_urlencoded/std\"]}}", + "url_2.5.8": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"form_urlencoded\",\"req\":\"^1.2.2\"},{\"default_features\":false,\"features\":[\"alloc\",\"compiled_data\"],\"name\":\"idna\",\"req\":\"^1.1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"percent-encoding\",\"req\":\"^2.3.2\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_derive\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"}],\"features\":{\"debugger_visualizer\":[],\"default\":[\"std\"],\"expose_internals\":[],\"serde\":[\"dep:serde\",\"dep:serde_derive\"],\"std\":[\"idna/std\",\"percent-encoding/std\",\"form_urlencoded/std\",\"serde?/std\"]}}", "urlencoding_2.1.3": "{\"dependencies\":[],\"features\":{}}", "utf-8_0.7.6": "{\"dependencies\":[],\"features\":{}}", "utf8_iter_1.0.4": "{\"dependencies\":[],\"features\":{}}", "utf8parse_0.2.2": "{\"dependencies\":[],\"features\":{\"default\":[],\"nightly\":[]}}", - "uuid_1.18.1": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.1.3\"},{\"default_features\":false,\"name\":\"atomic\",\"optional\":true,\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"borsh\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"name\":\"borsh-derive\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"name\":\"bytemuck\",\"optional\":true,\"req\":\"^1.18.1\"},{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"), target_feature = \\\"atomics\\\"))\"},{\"default_features\":false,\"name\":\"md-5\",\"optional\":true,\"req\":\"^0.10\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.56\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.79\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.56\"},{\"default_features\":false,\"name\":\"sha1_smol\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"slog\",\"optional\":true,\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.52\"},{\"name\":\"uuid-macro-internal\",\"optional\":true,\"req\":\"^1.18.1\"},{\"name\":\"uuid-rng-internal-lib\",\"optional\":true,\"package\":\"uuid-rng-internal\",\"req\":\"^1.18.1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"default_features\":false,\"features\":[\"msrv\"],\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen\",\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"features\":[\"derive\"],\"name\":\"zerocopy\",\"optional\":true,\"req\":\"^0.8\"}],\"features\":{\"atomic\":[\"dep:atomic\"],\"borsh\":[\"dep:borsh\",\"dep:borsh-derive\"],\"default\":[\"std\"],\"fast-rng\":[\"rng\",\"dep:rand\"],\"js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"],\"macro-diagnostics\":[\"dep:uuid-macro-internal\"],\"md5\":[\"dep:md-5\"],\"rng\":[\"dep:getrandom\"],\"rng-getrandom\":[\"rng\",\"dep:getrandom\",\"uuid-rng-internal-lib\",\"uuid-rng-internal-lib/getrandom\"],\"rng-rand\":[\"rng\",\"dep:rand\",\"uuid-rng-internal-lib\",\"uuid-rng-internal-lib/rand\"],\"sha1\":[\"dep:sha1_smol\"],\"std\":[\"wasm-bindgen?/std\",\"js-sys?/std\"],\"v1\":[\"atomic\"],\"v3\":[\"md5\"],\"v4\":[\"rng\"],\"v5\":[\"sha1\"],\"v6\":[\"atomic\"],\"v7\":[\"rng\"],\"v8\":[]}}", + "uuid_1.20.0": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.1.3\"},{\"default_features\":false,\"name\":\"atomic\",\"optional\":true,\"req\":\"^0.6\"},{\"default_features\":false,\"name\":\"borsh\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"name\":\"borsh-derive\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"name\":\"bytemuck\",\"optional\":true,\"req\":\"^1.20.0\"},{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"default_features\":false,\"name\":\"js-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"), target_feature = \\\"atomics\\\"))\"},{\"default_features\":false,\"name\":\"md-5\",\"optional\":true,\"req\":\"^0.10\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.9\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\"))))\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.221\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.221\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0.221\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0.56\"},{\"default_features\":false,\"name\":\"sha1_smol\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"slog\",\"optional\":true,\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.52\"},{\"name\":\"uuid-rng-internal-lib\",\"optional\":true,\"package\":\"uuid-rng-internal\",\"req\":\"^1.20.0\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"default_features\":false,\"features\":[\"msrv\"],\"name\":\"wasm-bindgen\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen\",\"req\":\"^0.2\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", any(target_os = \\\"unknown\\\", target_os = \\\"none\\\")))\"},{\"features\":[\"derive\"],\"name\":\"zerocopy\",\"optional\":true,\"req\":\"^0.8\"}],\"features\":{\"atomic\":[\"dep:atomic\"],\"borsh\":[\"dep:borsh\",\"dep:borsh-derive\"],\"default\":[\"std\"],\"fast-rng\":[\"rng\",\"dep:rand\"],\"js\":[\"dep:wasm-bindgen\",\"dep:js-sys\"],\"macro-diagnostics\":[],\"md5\":[\"dep:md-5\"],\"rng\":[\"dep:getrandom\"],\"rng-getrandom\":[\"rng\",\"dep:getrandom\",\"uuid-rng-internal-lib\",\"uuid-rng-internal-lib/getrandom\"],\"rng-rand\":[\"rng\",\"dep:rand\",\"uuid-rng-internal-lib\",\"uuid-rng-internal-lib/rand\"],\"serde\":[\"dep:serde_core\"],\"sha1\":[\"dep:sha1_smol\"],\"std\":[\"wasm-bindgen?/std\",\"js-sys?/std\"],\"v1\":[\"atomic\"],\"v3\":[\"md5\"],\"v4\":[\"rng\"],\"v5\":[\"sha1\"],\"v6\":[\"atomic\"],\"v7\":[\"rng\"],\"v8\":[]}}", "valuable_0.1.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"name\":\"valuable-derive\",\"optional\":true,\"req\":\"=0.1.1\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"derive\":[\"valuable-derive\"],\"std\":[\"alloc\"]}}", "vcpkg_0.2.15": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tempdir\",\"req\":\"^0.3.7\"}],\"features\":{}}", "version_check_0.9.5": "{\"dependencies\":[],\"features\":{}}", @@ -1283,131 +1340,134 @@ "walkdir_2.5.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"name\":\"same-file\",\"req\":\"^1.0.1\"},{\"name\":\"winapi-util\",\"req\":\"^0.1.1\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "want_0.3.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"tokio-executor\",\"req\":\"^0.2.0-alpha.2\"},{\"kind\":\"dev\",\"name\":\"tokio-sync\",\"req\":\"^0.2.0-alpha.2\"},{\"name\":\"try-lock\",\"req\":\"^0.2.4\"}],\"features\":{}}", "wasi_0.11.1+wasi-snapshot-preview1": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"rustc-std-workspace-alloc\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"rustc-dep-of-std\":[\"core\",\"rustc-std-workspace-alloc\"],\"std\":[]}}", - "wasi_0.14.2+wasi-0.2.4": "{\"dependencies\":[{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"rustc-std-workspace-alloc\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"bitflags\"],\"name\":\"wit-bindgen-rt\",\"req\":\"^0.39.0\"}],\"features\":{\"default\":[\"std\"],\"rustc-dep-of-std\":[\"compiler_builtins\",\"core\",\"rustc-std-workspace-alloc\"],\"std\":[]}}", + "wasip2_1.0.2+wasi-0.2.9": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"wit-bindgen\",\"req\":\"^0.51.0\"}],\"features\":{\"bitflags\":[\"wit-bindgen/bitflags\"],\"default\":[\"std\",\"bitflags\"],\"rustc-dep-of-std\":[\"core\",\"alloc\",\"wit-bindgen/rustc-dep-of-std\"],\"std\":[]}}", "wasite_0.1.0": "{\"dependencies\":[],\"features\":{}}", - "wasm-bindgen-backend_0.2.100": "{\"dependencies\":[{\"name\":\"bumpalo\",\"req\":\"^3.0.0\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"name\":\"wasm-bindgen-shared\",\"req\":\"=0.2.100\"}],\"features\":{\"extra-traits\":[\"syn/extra-traits\"]}}", - "wasm-bindgen-futures_0.4.50": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3.8\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"js-sys\",\"req\":\"=0.3.77\"},{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.100\"},{\"default_features\":false,\"features\":[\"MessageEvent\",\"Worker\"],\"name\":\"web-sys\",\"req\":\"=0.3.77\",\"target\":\"cfg(target_feature = \\\"atomics\\\")\"}],\"features\":{\"default\":[\"std\"],\"futures-core-03-stream\":[\"futures-core\"],\"std\":[\"wasm-bindgen/std\",\"js-sys/std\",\"web-sys/std\"]}}", - "wasm-bindgen-macro-support_0.2.100": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"visit\",\"visit-mut\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"name\":\"wasm-bindgen-backend\",\"req\":\"=0.2.100\"},{\"name\":\"wasm-bindgen-shared\",\"req\":\"=0.2.100\"}],\"features\":{\"extra-traits\":[\"syn/extra-traits\"],\"strict-macro\":[]}}", - "wasm-bindgen-macro_0.2.100": "{\"dependencies\":[{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"name\":\"wasm-bindgen-macro-support\",\"req\":\"=0.2.100\"}],\"features\":{\"strict-macro\":[\"wasm-bindgen-macro-support/strict-macro\"],\"xxx_debug_only_print_generated_code\":[]}}", - "wasm-bindgen-shared_0.2.100": "{\"dependencies\":[{\"name\":\"unicode-ident\",\"req\":\"^1.0.5\"}],\"features\":{}}", - "wasm-bindgen_0.2.100": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"rustversion\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"wasm-bindgen-macro\",\"req\":\"=0.2.100\"}],\"features\":{\"default\":[\"std\",\"msrv\"],\"enable-interning\":[\"std\"],\"gg-alloc\":[],\"msrv\":[\"rustversion\"],\"serde-serialize\":[\"serde\",\"serde_json\",\"std\"],\"spans\":[],\"std\":[],\"strict-macro\":[\"wasm-bindgen-macro/strict-macro\"],\"xxx_debug_only_print_generated_code\":[\"wasm-bindgen-macro/xxx_debug_only_print_generated_code\"]}}", + "wasm-bindgen-futures_0.4.58": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3.8\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-lite\",\"req\":\"^2\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures-util\",\"optional\":true,\"req\":\"^0.3.31\"},{\"default_features\":false,\"name\":\"js-sys\",\"req\":\"=0.3.85\"},{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.108\"},{\"default_features\":false,\"features\":[\"MessageEvent\",\"Worker\"],\"name\":\"web-sys\",\"req\":\"=0.3.85\",\"target\":\"cfg(target_feature = \\\"atomics\\\")\"}],\"features\":{\"default\":[\"std\"],\"futures-core-03-stream\":[\"futures-core\"],\"std\":[\"wasm-bindgen/std\",\"js-sys/std\",\"web-sys/std\",\"futures-util\"]}}", + "wasm-bindgen-macro-support_0.2.108": "{\"dependencies\":[{\"name\":\"bumpalo\",\"req\":\"^3.0.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"features\":[\"visit\",\"visit-mut\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0\"},{\"name\":\"wasm-bindgen-shared\",\"req\":\"=0.2.108\"}],\"features\":{\"extra-traits\":[\"syn/extra-traits\"],\"strict-macro\":[]}}", + "wasm-bindgen-macro_0.2.108": "{\"dependencies\":[{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"},{\"name\":\"wasm-bindgen-macro-support\",\"req\":\"=0.2.108\"}],\"features\":{\"strict-macro\":[\"wasm-bindgen-macro-support/strict-macro\"]}}", + "wasm-bindgen-shared_0.2.108": "{\"dependencies\":[{\"name\":\"unicode-ident\",\"req\":\"^1.0.5\"}],\"features\":{}}", + "wasm-bindgen_0.2.108": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"kind\":\"build\",\"name\":\"rustversion-compat\",\"package\":\"rustversion\",\"req\":\"^1.0.6\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1.0\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"wasm-bindgen-macro\",\"req\":\"=0.2.108\"},{\"name\":\"wasm-bindgen-shared\",\"req\":\"=0.2.108\"}],\"features\":{\"default\":[\"std\"],\"enable-interning\":[\"std\"],\"gg-alloc\":[],\"msrv\":[],\"rustversion\":[],\"serde-serialize\":[\"serde\",\"serde_json\",\"std\"],\"spans\":[],\"std\":[],\"strict-macro\":[\"wasm-bindgen-macro/strict-macro\"],\"xxx_debug_only_print_generated_code\":[]}}", "wasm-streams_0.4.2": "{\"dependencies\":[{\"features\":[\"io\",\"sink\"],\"name\":\"futures-util\",\"req\":\"^0.3.31\"},{\"features\":[\"futures\"],\"kind\":\"dev\",\"name\":\"gloo-timers\",\"req\":\"^0.3.0\"},{\"name\":\"js-sys\",\"req\":\"^0.3.72\"},{\"kind\":\"dev\",\"name\":\"pin-project\",\"req\":\"^1\"},{\"features\":[\"macros\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"wasm-bindgen\",\"req\":\"^0.2.95\"},{\"name\":\"wasm-bindgen-futures\",\"req\":\"^0.4.45\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.45\"},{\"features\":[\"AbortSignal\",\"QueuingStrategy\",\"ReadableStream\",\"ReadableStreamType\",\"ReadableWritablePair\",\"ReadableStreamByobReader\",\"ReadableStreamReaderMode\",\"ReadableStreamReadResult\",\"ReadableStreamByobRequest\",\"ReadableStreamDefaultReader\",\"ReadableByteStreamController\",\"ReadableStreamGetReaderOptions\",\"ReadableStreamDefaultController\",\"StreamPipeOptions\",\"TransformStream\",\"TransformStreamDefaultController\",\"Transformer\",\"UnderlyingSink\",\"UnderlyingSource\",\"WritableStream\",\"WritableStreamDefaultController\",\"WritableStreamDefaultWriter\"],\"name\":\"web-sys\",\"req\":\"^0.3.72\"},{\"features\":[\"console\",\"AbortSignal\",\"ErrorEvent\",\"PromiseRejectionEvent\",\"Response\",\"ReadableStream\",\"Window\"],\"kind\":\"dev\",\"name\":\"web-sys\",\"req\":\"^0.3.72\"}],\"features\":{}}", - "wayland-backend_0.3.11": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"concat-idents\",\"req\":\"^1.1\"},{\"name\":\"downcast-rs\",\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"raw-window-handle\",\"optional\":true,\"req\":\"^0.5.0\"},{\"features\":[\"event\",\"fs\",\"net\",\"process\"],\"name\":\"rustix\",\"req\":\"^1.0.2\"},{\"name\":\"rwh_06\",\"optional\":true,\"package\":\"raw-window-handle\",\"req\":\"^0.6.0\"},{\"name\":\"scoped-tls\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"scoped-tls\",\"req\":\"^1.0\"},{\"features\":[\"union\",\"const_generics\",\"const_new\"],\"name\":\"smallvec\",\"req\":\"^1.9\"},{\"name\":\"wayland-sys\",\"req\":\"^0.31.7\"}],\"features\":{\"client_system\":[\"wayland-sys/client\",\"dep:scoped-tls\"],\"dlopen\":[\"wayland-sys/dlopen\"],\"server_system\":[\"wayland-sys/server\",\"dep:scoped-tls\"]}}", - "wayland-client_0.31.11": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3.16\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"features\":[\"event\"],\"name\":\"rustix\",\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.2\"},{\"name\":\"wayland-backend\",\"req\":\"^0.3.11\"},{\"name\":\"wayland-scanner\",\"req\":\"^0.31.7\"}],\"features\":{}}", - "wayland-protocols-wlr_0.3.9": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"name\":\"wayland-backend\",\"req\":\"^0.3.11\"},{\"name\":\"wayland-client\",\"optional\":true,\"req\":\"^0.31.11\"},{\"name\":\"wayland-protocols\",\"req\":\"^0.32.9\"},{\"name\":\"wayland-scanner\",\"req\":\"^0.31.7\"},{\"name\":\"wayland-server\",\"optional\":true,\"req\":\"^0.31.10\"}],\"features\":{\"client\":[\"wayland-client\",\"wayland-protocols/client\"],\"server\":[\"wayland-server\",\"wayland-protocols/server\"]}}", - "wayland-protocols_0.32.9": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"name\":\"wayland-backend\",\"req\":\"^0.3.11\"},{\"name\":\"wayland-client\",\"optional\":true,\"req\":\"^0.31.11\"},{\"name\":\"wayland-scanner\",\"req\":\"^0.31.7\"},{\"name\":\"wayland-server\",\"optional\":true,\"req\":\"^0.31.10\"}],\"features\":{\"client\":[\"wayland-client\"],\"server\":[\"wayland-server\"],\"staging\":[],\"unstable\":[]}}", - "wayland-scanner_0.31.7": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.11\"},{\"name\":\"quick-xml\",\"req\":\"^0.37.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"similar\",\"req\":\"^2\"}],\"features\":{}}", - "wayland-sys_0.31.7": "{\"dependencies\":[{\"name\":\"dlib\",\"optional\":true,\"req\":\"^0.5.1\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"memoffset\",\"optional\":true,\"req\":\"^0.9\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.7\"}],\"features\":{\"client\":[\"dep:dlib\",\"dep:log\"],\"cursor\":[\"client\"],\"dlopen\":[\"once_cell\"],\"egl\":[\"client\"],\"server\":[\"libc\",\"memoffset\",\"dep:dlib\",\"dep:log\"]}}", - "web-sys_0.3.77": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"js-sys\",\"req\":\"=0.3.77\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.100\"}],\"features\":{\"AbortController\":[],\"AbortSignal\":[\"EventTarget\"],\"AddEventListenerOptions\":[],\"AesCbcParams\":[],\"AesCtrParams\":[],\"AesDerivedKeyParams\":[],\"AesGcmParams\":[],\"AesKeyAlgorithm\":[],\"AesKeyGenParams\":[],\"Algorithm\":[],\"AlignSetting\":[],\"AllowedBluetoothDevice\":[],\"AllowedUsbDevice\":[],\"AlphaOption\":[],\"AnalyserNode\":[\"AudioNode\",\"EventTarget\"],\"AnalyserOptions\":[],\"AngleInstancedArrays\":[],\"Animation\":[\"EventTarget\"],\"AnimationEffect\":[],\"AnimationEvent\":[\"Event\"],\"AnimationEventInit\":[],\"AnimationPlayState\":[],\"AnimationPlaybackEvent\":[\"Event\"],\"AnimationPlaybackEventInit\":[],\"AnimationPropertyDetails\":[],\"AnimationPropertyValueDetails\":[],\"AnimationTimeline\":[],\"AssignedNodesOptions\":[],\"AttestationConveyancePreference\":[],\"Attr\":[\"EventTarget\",\"Node\"],\"AttributeNameValue\":[],\"AudioBuffer\":[],\"AudioBufferOptions\":[],\"AudioBufferSourceNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"AudioBufferSourceOptions\":[],\"AudioConfiguration\":[],\"AudioContext\":[\"BaseAudioContext\",\"EventTarget\"],\"AudioContextLatencyCategory\":[],\"AudioContextOptions\":[],\"AudioContextState\":[],\"AudioData\":[],\"AudioDataCopyToOptions\":[],\"AudioDataInit\":[],\"AudioDecoder\":[],\"AudioDecoderConfig\":[],\"AudioDecoderInit\":[],\"AudioDecoderSupport\":[],\"AudioDestinationNode\":[\"AudioNode\",\"EventTarget\"],\"AudioEncoder\":[],\"AudioEncoderConfig\":[],\"AudioEncoderInit\":[],\"AudioEncoderSupport\":[],\"AudioListener\":[],\"AudioNode\":[\"EventTarget\"],\"AudioNodeOptions\":[],\"AudioParam\":[],\"AudioParamMap\":[],\"AudioProcessingEvent\":[\"Event\"],\"AudioSampleFormat\":[],\"AudioScheduledSourceNode\":[\"AudioNode\",\"EventTarget\"],\"AudioSinkInfo\":[],\"AudioSinkOptions\":[],\"AudioSinkType\":[],\"AudioStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"AudioTrack\":[],\"AudioTrackList\":[\"EventTarget\"],\"AudioWorklet\":[\"Worklet\"],\"AudioWorkletGlobalScope\":[\"WorkletGlobalScope\"],\"AudioWorkletNode\":[\"AudioNode\",\"EventTarget\"],\"AudioWorkletNodeOptions\":[],\"AudioWorkletProcessor\":[],\"AuthenticationExtensionsClientInputs\":[],\"AuthenticationExtensionsClientInputsJson\":[],\"AuthenticationExtensionsClientOutputs\":[],\"AuthenticationExtensionsClientOutputsJson\":[],\"AuthenticationExtensionsDevicePublicKeyInputs\":[],\"AuthenticationExtensionsDevicePublicKeyOutputs\":[],\"AuthenticationExtensionsLargeBlobInputs\":[],\"AuthenticationExtensionsLargeBlobOutputs\":[],\"AuthenticationExtensionsPrfInputs\":[],\"AuthenticationExtensionsPrfOutputs\":[],\"AuthenticationExtensionsPrfValues\":[],\"AuthenticationResponseJson\":[],\"AuthenticatorAssertionResponse\":[\"AuthenticatorResponse\"],\"AuthenticatorAssertionResponseJson\":[],\"AuthenticatorAttachment\":[],\"AuthenticatorAttestationResponse\":[\"AuthenticatorResponse\"],\"AuthenticatorAttestationResponseJson\":[],\"AuthenticatorResponse\":[],\"AuthenticatorSelectionCriteria\":[],\"AuthenticatorTransport\":[],\"AutoKeyword\":[],\"AutocompleteInfo\":[],\"BarProp\":[],\"BaseAudioContext\":[\"EventTarget\"],\"BaseComputedKeyframe\":[],\"BaseKeyframe\":[],\"BasePropertyIndexedKeyframe\":[],\"BasicCardRequest\":[],\"BasicCardResponse\":[],\"BasicCardType\":[],\"BatteryManager\":[\"EventTarget\"],\"BeforeUnloadEvent\":[\"Event\"],\"BinaryType\":[],\"BiquadFilterNode\":[\"AudioNode\",\"EventTarget\"],\"BiquadFilterOptions\":[],\"BiquadFilterType\":[],\"Blob\":[],\"BlobEvent\":[\"Event\"],\"BlobEventInit\":[],\"BlobPropertyBag\":[],\"BlockParsingOptions\":[],\"Bluetooth\":[\"EventTarget\"],\"BluetoothAdvertisingEvent\":[\"Event\"],\"BluetoothAdvertisingEventInit\":[],\"BluetoothCharacteristicProperties\":[],\"BluetoothDataFilterInit\":[],\"BluetoothDevice\":[\"EventTarget\"],\"BluetoothLeScanFilterInit\":[],\"BluetoothManufacturerDataMap\":[],\"BluetoothPermissionDescriptor\":[],\"BluetoothPermissionResult\":[\"EventTarget\",\"PermissionStatus\"],\"BluetoothPermissionStorage\":[],\"BluetoothRemoteGattCharacteristic\":[\"EventTarget\"],\"BluetoothRemoteGattDescriptor\":[],\"BluetoothRemoteGattServer\":[],\"BluetoothRemoteGattService\":[\"EventTarget\"],\"BluetoothServiceDataMap\":[],\"BluetoothUuid\":[],\"BoxQuadOptions\":[],\"BroadcastChannel\":[\"EventTarget\"],\"BrowserElementDownloadOptions\":[],\"BrowserElementExecuteScriptOptions\":[],\"BrowserFeedWriter\":[],\"BrowserFindCaseSensitivity\":[],\"BrowserFindDirection\":[],\"ByteLengthQueuingStrategy\":[],\"Cache\":[],\"CacheBatchOperation\":[],\"CacheQueryOptions\":[],\"CacheStorage\":[],\"CacheStorageNamespace\":[],\"CanvasCaptureMediaStream\":[\"EventTarget\",\"MediaStream\"],\"CanvasCaptureMediaStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"CanvasGradient\":[],\"CanvasPattern\":[],\"CanvasRenderingContext2d\":[],\"CanvasWindingRule\":[],\"CaretChangedReason\":[],\"CaretPosition\":[],\"CaretStateChangedEventInit\":[],\"CdataSection\":[\"CharacterData\",\"EventTarget\",\"Node\",\"Text\"],\"ChannelCountMode\":[],\"ChannelInterpretation\":[],\"ChannelMergerNode\":[\"AudioNode\",\"EventTarget\"],\"ChannelMergerOptions\":[],\"ChannelSplitterNode\":[\"AudioNode\",\"EventTarget\"],\"ChannelSplitterOptions\":[],\"CharacterData\":[\"EventTarget\",\"Node\"],\"CheckerboardReason\":[],\"CheckerboardReport\":[],\"CheckerboardReportService\":[],\"ChromeFilePropertyBag\":[],\"ChromeWorker\":[\"EventTarget\",\"Worker\"],\"Client\":[],\"ClientQueryOptions\":[],\"ClientRectsAndTexts\":[],\"ClientType\":[],\"Clients\":[],\"Clipboard\":[\"EventTarget\"],\"ClipboardEvent\":[\"Event\"],\"ClipboardEventInit\":[],\"ClipboardItem\":[],\"ClipboardItemOptions\":[],\"ClipboardPermissionDescriptor\":[],\"ClipboardUnsanitizedFormats\":[],\"CloseEvent\":[\"Event\"],\"CloseEventInit\":[],\"CodecState\":[],\"CollectedClientData\":[],\"ColorSpaceConversion\":[],\"Comment\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"CompositeOperation\":[],\"CompositionEvent\":[\"Event\",\"UiEvent\"],\"CompositionEventInit\":[],\"CompressionFormat\":[],\"CompressionStream\":[],\"ComputedEffectTiming\":[],\"ConnStatusDict\":[],\"ConnectionType\":[],\"ConsoleCounter\":[],\"ConsoleCounterError\":[],\"ConsoleEvent\":[],\"ConsoleInstance\":[],\"ConsoleInstanceOptions\":[],\"ConsoleLevel\":[],\"ConsoleLogLevel\":[],\"ConsoleProfileEvent\":[],\"ConsoleStackEntry\":[],\"ConsoleTimerError\":[],\"ConsoleTimerLogOrEnd\":[],\"ConsoleTimerStart\":[],\"ConstantSourceNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"ConstantSourceOptions\":[],\"ConstrainBooleanParameters\":[],\"ConstrainDomStringParameters\":[],\"ConstrainDoubleRange\":[],\"ConstrainLongRange\":[],\"ContextAttributes2d\":[],\"ConvertCoordinateOptions\":[],\"ConvolverNode\":[\"AudioNode\",\"EventTarget\"],\"ConvolverOptions\":[],\"Coordinates\":[],\"CountQueuingStrategy\":[],\"Credential\":[],\"CredentialCreationOptions\":[],\"CredentialPropertiesOutput\":[],\"CredentialRequestOptions\":[],\"CredentialsContainer\":[],\"Crypto\":[],\"CryptoKey\":[],\"CryptoKeyPair\":[],\"CssAnimation\":[\"Animation\",\"EventTarget\"],\"CssBoxType\":[],\"CssConditionRule\":[\"CssGroupingRule\",\"CssRule\"],\"CssCounterStyleRule\":[\"CssRule\"],\"CssFontFaceRule\":[\"CssRule\"],\"CssFontFeatureValuesRule\":[\"CssRule\"],\"CssGroupingRule\":[\"CssRule\"],\"CssImportRule\":[\"CssRule\"],\"CssKeyframeRule\":[\"CssRule\"],\"CssKeyframesRule\":[\"CssRule\"],\"CssMediaRule\":[\"CssConditionRule\",\"CssGroupingRule\",\"CssRule\"],\"CssNamespaceRule\":[\"CssRule\"],\"CssPageRule\":[\"CssRule\"],\"CssPseudoElement\":[],\"CssRule\":[],\"CssRuleList\":[],\"CssStyleDeclaration\":[],\"CssStyleRule\":[\"CssRule\"],\"CssStyleSheet\":[\"StyleSheet\"],\"CssStyleSheetParsingMode\":[],\"CssSupportsRule\":[\"CssConditionRule\",\"CssGroupingRule\",\"CssRule\"],\"CssTransition\":[\"Animation\",\"EventTarget\"],\"CustomElementRegistry\":[],\"CustomEvent\":[\"Event\"],\"CustomEventInit\":[],\"DataTransfer\":[],\"DataTransferItem\":[],\"DataTransferItemList\":[],\"DateTimeValue\":[],\"DecoderDoctorNotification\":[],\"DecoderDoctorNotificationType\":[],\"DecompressionStream\":[],\"DedicatedWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"DelayNode\":[\"AudioNode\",\"EventTarget\"],\"DelayOptions\":[],\"DeviceAcceleration\":[],\"DeviceAccelerationInit\":[],\"DeviceLightEvent\":[\"Event\"],\"DeviceLightEventInit\":[],\"DeviceMotionEvent\":[\"Event\"],\"DeviceMotionEventInit\":[],\"DeviceOrientationEvent\":[\"Event\"],\"DeviceOrientationEventInit\":[],\"DeviceProximityEvent\":[\"Event\"],\"DeviceProximityEventInit\":[],\"DeviceRotationRate\":[],\"DeviceRotationRateInit\":[],\"DhKeyDeriveParams\":[],\"DirectionSetting\":[],\"Directory\":[],\"DirectoryPickerOptions\":[],\"DisplayMediaStreamConstraints\":[],\"DisplayNameOptions\":[],\"DisplayNameResult\":[],\"DistanceModelType\":[],\"DnsCacheDict\":[],\"DnsCacheEntry\":[],\"DnsLookupDict\":[],\"Document\":[\"EventTarget\",\"Node\"],\"DocumentFragment\":[\"EventTarget\",\"Node\"],\"DocumentTimeline\":[\"AnimationTimeline\"],\"DocumentTimelineOptions\":[],\"DocumentType\":[\"EventTarget\",\"Node\"],\"DomError\":[],\"DomException\":[],\"DomImplementation\":[],\"DomMatrix\":[\"DomMatrixReadOnly\"],\"DomMatrix2dInit\":[],\"DomMatrixInit\":[],\"DomMatrixReadOnly\":[],\"DomParser\":[],\"DomPoint\":[\"DomPointReadOnly\"],\"DomPointInit\":[],\"DomPointReadOnly\":[],\"DomQuad\":[],\"DomQuadInit\":[],\"DomQuadJson\":[],\"DomRect\":[\"DomRectReadOnly\"],\"DomRectInit\":[],\"DomRectList\":[],\"DomRectReadOnly\":[],\"DomRequest\":[\"EventTarget\"],\"DomRequestReadyState\":[],\"DomStringList\":[],\"DomStringMap\":[],\"DomTokenList\":[],\"DomWindowResizeEventDetail\":[],\"DoubleRange\":[],\"DragEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"DragEventInit\":[],\"DynamicsCompressorNode\":[\"AudioNode\",\"EventTarget\"],\"DynamicsCompressorOptions\":[],\"EcKeyAlgorithm\":[],\"EcKeyGenParams\":[],\"EcKeyImportParams\":[],\"EcdhKeyDeriveParams\":[],\"EcdsaParams\":[],\"EffectTiming\":[],\"Element\":[\"EventTarget\",\"Node\"],\"ElementCreationOptions\":[],\"ElementDefinitionOptions\":[],\"EncodedAudioChunk\":[],\"EncodedAudioChunkInit\":[],\"EncodedAudioChunkMetadata\":[],\"EncodedAudioChunkType\":[],\"EncodedVideoChunk\":[],\"EncodedVideoChunkInit\":[],\"EncodedVideoChunkMetadata\":[],\"EncodedVideoChunkType\":[],\"EndingTypes\":[],\"ErrorCallback\":[],\"ErrorEvent\":[\"Event\"],\"ErrorEventInit\":[],\"Event\":[],\"EventInit\":[],\"EventListener\":[],\"EventListenerOptions\":[],\"EventModifierInit\":[],\"EventSource\":[\"EventTarget\"],\"EventSourceInit\":[],\"EventTarget\":[],\"Exception\":[],\"ExtBlendMinmax\":[],\"ExtColorBufferFloat\":[],\"ExtColorBufferHalfFloat\":[],\"ExtDisjointTimerQuery\":[],\"ExtFragDepth\":[],\"ExtSRgb\":[],\"ExtShaderTextureLod\":[],\"ExtTextureFilterAnisotropic\":[],\"ExtTextureNorm16\":[],\"ExtendableEvent\":[\"Event\"],\"ExtendableEventInit\":[],\"ExtendableMessageEvent\":[\"Event\",\"ExtendableEvent\"],\"ExtendableMessageEventInit\":[],\"External\":[],\"FakePluginMimeEntry\":[],\"FakePluginTagInit\":[],\"FetchEvent\":[\"Event\",\"ExtendableEvent\"],\"FetchEventInit\":[],\"FetchObserver\":[\"EventTarget\"],\"FetchReadableStreamReadDataArray\":[],\"FetchReadableStreamReadDataDone\":[],\"FetchState\":[],\"File\":[\"Blob\"],\"FileCallback\":[],\"FileList\":[],\"FilePickerAcceptType\":[],\"FilePickerOptions\":[],\"FilePropertyBag\":[],\"FileReader\":[\"EventTarget\"],\"FileReaderSync\":[],\"FileSystem\":[],\"FileSystemCreateWritableOptions\":[],\"FileSystemDirectoryEntry\":[\"FileSystemEntry\"],\"FileSystemDirectoryHandle\":[\"FileSystemHandle\"],\"FileSystemDirectoryReader\":[],\"FileSystemEntriesCallback\":[],\"FileSystemEntry\":[],\"FileSystemEntryCallback\":[],\"FileSystemFileEntry\":[\"FileSystemEntry\"],\"FileSystemFileHandle\":[\"FileSystemHandle\"],\"FileSystemFlags\":[],\"FileSystemGetDirectoryOptions\":[],\"FileSystemGetFileOptions\":[],\"FileSystemHandle\":[],\"FileSystemHandleKind\":[],\"FileSystemHandlePermissionDescriptor\":[],\"FileSystemPermissionDescriptor\":[],\"FileSystemPermissionMode\":[],\"FileSystemReadWriteOptions\":[],\"FileSystemRemoveOptions\":[],\"FileSystemSyncAccessHandle\":[],\"FileSystemWritableFileStream\":[\"WritableStream\"],\"FillMode\":[],\"FlashClassification\":[],\"FlowControlType\":[],\"FocusEvent\":[\"Event\",\"UiEvent\"],\"FocusEventInit\":[],\"FocusOptions\":[],\"FontData\":[],\"FontFace\":[],\"FontFaceDescriptors\":[],\"FontFaceLoadStatus\":[],\"FontFaceSet\":[\"EventTarget\"],\"FontFaceSetIterator\":[],\"FontFaceSetIteratorResult\":[],\"FontFaceSetLoadEvent\":[\"Event\"],\"FontFaceSetLoadEventInit\":[],\"FontFaceSetLoadStatus\":[],\"FormData\":[],\"FrameType\":[],\"FuzzingFunctions\":[],\"GainNode\":[\"AudioNode\",\"EventTarget\"],\"GainOptions\":[],\"Gamepad\":[],\"GamepadButton\":[],\"GamepadEffectParameters\":[],\"GamepadEvent\":[\"Event\"],\"GamepadEventInit\":[],\"GamepadHand\":[],\"GamepadHapticActuator\":[],\"GamepadHapticActuatorType\":[],\"GamepadHapticEffectType\":[],\"GamepadHapticsResult\":[],\"GamepadMappingType\":[],\"GamepadPose\":[],\"GamepadTouch\":[],\"Geolocation\":[],\"GetAnimationsOptions\":[],\"GetRootNodeOptions\":[],\"GetUserMediaRequest\":[],\"Gpu\":[],\"GpuAdapter\":[],\"GpuAdapterInfo\":[],\"GpuAddressMode\":[],\"GpuAutoLayoutMode\":[],\"GpuBindGroup\":[],\"GpuBindGroupDescriptor\":[],\"GpuBindGroupEntry\":[],\"GpuBindGroupLayout\":[],\"GpuBindGroupLayoutDescriptor\":[],\"GpuBindGroupLayoutEntry\":[],\"GpuBlendComponent\":[],\"GpuBlendFactor\":[],\"GpuBlendOperation\":[],\"GpuBlendState\":[],\"GpuBuffer\":[],\"GpuBufferBinding\":[],\"GpuBufferBindingLayout\":[],\"GpuBufferBindingType\":[],\"GpuBufferDescriptor\":[],\"GpuBufferMapState\":[],\"GpuCanvasAlphaMode\":[],\"GpuCanvasConfiguration\":[],\"GpuCanvasContext\":[],\"GpuCanvasToneMapping\":[],\"GpuCanvasToneMappingMode\":[],\"GpuColorDict\":[],\"GpuColorTargetState\":[],\"GpuCommandBuffer\":[],\"GpuCommandBufferDescriptor\":[],\"GpuCommandEncoder\":[],\"GpuCommandEncoderDescriptor\":[],\"GpuCompareFunction\":[],\"GpuCompilationInfo\":[],\"GpuCompilationMessage\":[],\"GpuCompilationMessageType\":[],\"GpuComputePassDescriptor\":[],\"GpuComputePassEncoder\":[],\"GpuComputePassTimestampWrites\":[],\"GpuComputePipeline\":[],\"GpuComputePipelineDescriptor\":[],\"GpuCopyExternalImageDestInfo\":[],\"GpuCopyExternalImageSourceInfo\":[],\"GpuCullMode\":[],\"GpuDepthStencilState\":[],\"GpuDevice\":[\"EventTarget\"],\"GpuDeviceDescriptor\":[],\"GpuDeviceLostInfo\":[],\"GpuDeviceLostReason\":[],\"GpuError\":[],\"GpuErrorFilter\":[],\"GpuExtent3dDict\":[],\"GpuExternalTexture\":[],\"GpuExternalTextureBindingLayout\":[],\"GpuExternalTextureDescriptor\":[],\"GpuFeatureName\":[],\"GpuFilterMode\":[],\"GpuFragmentState\":[],\"GpuFrontFace\":[],\"GpuIndexFormat\":[],\"GpuInternalError\":[\"GpuError\"],\"GpuLoadOp\":[],\"GpuMipmapFilterMode\":[],\"GpuMultisampleState\":[],\"GpuObjectDescriptorBase\":[],\"GpuOrigin2dDict\":[],\"GpuOrigin3dDict\":[],\"GpuOutOfMemoryError\":[\"GpuError\"],\"GpuPipelineDescriptorBase\":[],\"GpuPipelineError\":[\"DomException\"],\"GpuPipelineErrorInit\":[],\"GpuPipelineErrorReason\":[],\"GpuPipelineLayout\":[],\"GpuPipelineLayoutDescriptor\":[],\"GpuPowerPreference\":[],\"GpuPrimitiveState\":[],\"GpuPrimitiveTopology\":[],\"GpuProgrammableStage\":[],\"GpuQuerySet\":[],\"GpuQuerySetDescriptor\":[],\"GpuQueryType\":[],\"GpuQueue\":[],\"GpuQueueDescriptor\":[],\"GpuRenderBundle\":[],\"GpuRenderBundleDescriptor\":[],\"GpuRenderBundleEncoder\":[],\"GpuRenderBundleEncoderDescriptor\":[],\"GpuRenderPassColorAttachment\":[],\"GpuRenderPassDepthStencilAttachment\":[],\"GpuRenderPassDescriptor\":[],\"GpuRenderPassEncoder\":[],\"GpuRenderPassLayout\":[],\"GpuRenderPassTimestampWrites\":[],\"GpuRenderPipeline\":[],\"GpuRenderPipelineDescriptor\":[],\"GpuRequestAdapterOptions\":[],\"GpuSampler\":[],\"GpuSamplerBindingLayout\":[],\"GpuSamplerBindingType\":[],\"GpuSamplerDescriptor\":[],\"GpuShaderModule\":[],\"GpuShaderModuleCompilationHint\":[],\"GpuShaderModuleDescriptor\":[],\"GpuStencilFaceState\":[],\"GpuStencilOperation\":[],\"GpuStorageTextureAccess\":[],\"GpuStorageTextureBindingLayout\":[],\"GpuStoreOp\":[],\"GpuSupportedFeatures\":[],\"GpuSupportedLimits\":[],\"GpuTexelCopyBufferInfo\":[],\"GpuTexelCopyBufferLayout\":[],\"GpuTexelCopyTextureInfo\":[],\"GpuTexture\":[],\"GpuTextureAspect\":[],\"GpuTextureBindingLayout\":[],\"GpuTextureDescriptor\":[],\"GpuTextureDimension\":[],\"GpuTextureFormat\":[],\"GpuTextureSampleType\":[],\"GpuTextureView\":[],\"GpuTextureViewDescriptor\":[],\"GpuTextureViewDimension\":[],\"GpuUncapturedErrorEvent\":[\"Event\"],\"GpuUncapturedErrorEventInit\":[],\"GpuValidationError\":[\"GpuError\"],\"GpuVertexAttribute\":[],\"GpuVertexBufferLayout\":[],\"GpuVertexFormat\":[],\"GpuVertexState\":[],\"GpuVertexStepMode\":[],\"GroupedHistoryEventInit\":[],\"HalfOpenInfoDict\":[],\"HardwareAcceleration\":[],\"HashChangeEvent\":[\"Event\"],\"HashChangeEventInit\":[],\"Headers\":[],\"HeadersGuardEnum\":[],\"Hid\":[\"EventTarget\"],\"HidCollectionInfo\":[],\"HidConnectionEvent\":[\"Event\"],\"HidConnectionEventInit\":[],\"HidDevice\":[\"EventTarget\"],\"HidDeviceFilter\":[],\"HidDeviceRequestOptions\":[],\"HidInputReportEvent\":[\"Event\"],\"HidInputReportEventInit\":[],\"HidReportInfo\":[],\"HidReportItem\":[],\"HidUnitSystem\":[],\"HiddenPluginEventInit\":[],\"History\":[],\"HitRegionOptions\":[],\"HkdfParams\":[],\"HmacDerivedKeyParams\":[],\"HmacImportParams\":[],\"HmacKeyAlgorithm\":[],\"HmacKeyGenParams\":[],\"HtmlAllCollection\":[],\"HtmlAnchorElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlAreaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlAudioElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"HtmlMediaElement\",\"Node\"],\"HtmlBaseElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlBodyElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlBrElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlButtonElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlCanvasElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlCollection\":[],\"HtmlDListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDataElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDataListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDetailsElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDialogElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDirectoryElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDivElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDocument\":[\"Document\",\"EventTarget\",\"Node\"],\"HtmlElement\":[\"Element\",\"EventTarget\",\"Node\"],\"HtmlEmbedElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFieldSetElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFontElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFormControlsCollection\":[\"HtmlCollection\"],\"HtmlFormElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFrameElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFrameSetElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHeadElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHeadingElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHrElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHtmlElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlIFrameElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlImageElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlInputElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLabelElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLegendElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLiElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLinkElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMapElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMediaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMenuElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMenuItemElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMetaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMeterElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlModElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlObjectElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptGroupElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptionsCollection\":[\"HtmlCollection\"],\"HtmlOutputElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlParagraphElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlParamElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlPictureElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlPreElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlProgressElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlQuoteElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlScriptElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSelectElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSlotElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSourceElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSpanElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlStyleElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableCaptionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableCellElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableColElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableRowElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableSectionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTemplateElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTextAreaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTimeElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTitleElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTrackElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlUListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlUnknownElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlVideoElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"HtmlMediaElement\",\"Node\"],\"HttpConnDict\":[],\"HttpConnInfo\":[],\"HttpConnectionElement\":[],\"IdbCursor\":[],\"IdbCursorDirection\":[],\"IdbCursorWithValue\":[\"IdbCursor\"],\"IdbDatabase\":[\"EventTarget\"],\"IdbFactory\":[],\"IdbFileHandle\":[\"EventTarget\"],\"IdbFileMetadataParameters\":[],\"IdbFileRequest\":[\"DomRequest\",\"EventTarget\"],\"IdbIndex\":[],\"IdbIndexParameters\":[],\"IdbKeyRange\":[],\"IdbLocaleAwareKeyRange\":[\"IdbKeyRange\"],\"IdbMutableFile\":[\"EventTarget\"],\"IdbObjectStore\":[],\"IdbObjectStoreParameters\":[],\"IdbOpenDbOptions\":[],\"IdbOpenDbRequest\":[\"EventTarget\",\"IdbRequest\"],\"IdbRequest\":[\"EventTarget\"],\"IdbRequestReadyState\":[],\"IdbTransaction\":[\"EventTarget\"],\"IdbTransactionDurability\":[],\"IdbTransactionMode\":[],\"IdbTransactionOptions\":[],\"IdbVersionChangeEvent\":[\"Event\"],\"IdbVersionChangeEventInit\":[],\"IdleDeadline\":[],\"IdleRequestOptions\":[],\"IirFilterNode\":[\"AudioNode\",\"EventTarget\"],\"IirFilterOptions\":[],\"ImageBitmap\":[],\"ImageBitmapOptions\":[],\"ImageBitmapRenderingContext\":[],\"ImageCapture\":[],\"ImageCaptureError\":[],\"ImageCaptureErrorEvent\":[\"Event\"],\"ImageCaptureErrorEventInit\":[],\"ImageData\":[],\"ImageDecodeOptions\":[],\"ImageDecodeResult\":[],\"ImageDecoder\":[],\"ImageDecoderInit\":[],\"ImageEncodeOptions\":[],\"ImageOrientation\":[],\"ImageTrack\":[\"EventTarget\"],\"ImageTrackList\":[],\"InputDeviceInfo\":[\"MediaDeviceInfo\"],\"InputEvent\":[\"Event\",\"UiEvent\"],\"InputEventInit\":[],\"IntersectionObserver\":[],\"IntersectionObserverEntry\":[],\"IntersectionObserverEntryInit\":[],\"IntersectionObserverInit\":[],\"IntlUtils\":[],\"IsInputPendingOptions\":[],\"IterableKeyAndValueResult\":[],\"IterableKeyOrValueResult\":[],\"IterationCompositeOperation\":[],\"JsonWebKey\":[],\"KeyAlgorithm\":[],\"KeyEvent\":[],\"KeyFrameRequestEvent\":[\"Event\"],\"KeyIdsInitData\":[],\"KeyboardEvent\":[\"Event\",\"UiEvent\"],\"KeyboardEventInit\":[],\"KeyframeAnimationOptions\":[],\"KeyframeEffect\":[\"AnimationEffect\"],\"KeyframeEffectOptions\":[],\"L10nElement\":[],\"L10nValue\":[],\"LargeBlobSupport\":[],\"LatencyMode\":[],\"LifecycleCallbacks\":[],\"LineAlignSetting\":[],\"ListBoxObject\":[],\"LocalMediaStream\":[\"EventTarget\",\"MediaStream\"],\"LocaleInfo\":[],\"Location\":[],\"Lock\":[],\"LockInfo\":[],\"LockManager\":[],\"LockManagerSnapshot\":[],\"LockMode\":[],\"LockOptions\":[],\"MathMlElement\":[\"Element\",\"EventTarget\",\"Node\"],\"MediaCapabilities\":[],\"MediaCapabilitiesInfo\":[],\"MediaConfiguration\":[],\"MediaDecodingConfiguration\":[],\"MediaDecodingType\":[],\"MediaDeviceInfo\":[],\"MediaDeviceKind\":[],\"MediaDevices\":[\"EventTarget\"],\"MediaElementAudioSourceNode\":[\"AudioNode\",\"EventTarget\"],\"MediaElementAudioSourceOptions\":[],\"MediaEncodingConfiguration\":[],\"MediaEncodingType\":[],\"MediaEncryptedEvent\":[\"Event\"],\"MediaError\":[],\"MediaImage\":[],\"MediaKeyError\":[\"Event\"],\"MediaKeyMessageEvent\":[\"Event\"],\"MediaKeyMessageEventInit\":[],\"MediaKeyMessageType\":[],\"MediaKeyNeededEventInit\":[],\"MediaKeySession\":[\"EventTarget\"],\"MediaKeySessionType\":[],\"MediaKeyStatus\":[],\"MediaKeyStatusMap\":[],\"MediaKeySystemAccess\":[],\"MediaKeySystemConfiguration\":[],\"MediaKeySystemMediaCapability\":[],\"MediaKeySystemStatus\":[],\"MediaKeys\":[],\"MediaKeysPolicy\":[],\"MediaKeysRequirement\":[],\"MediaList\":[],\"MediaMetadata\":[],\"MediaMetadataInit\":[],\"MediaPositionState\":[],\"MediaQueryList\":[\"EventTarget\"],\"MediaQueryListEvent\":[\"Event\"],\"MediaQueryListEventInit\":[],\"MediaRecorder\":[\"EventTarget\"],\"MediaRecorderErrorEvent\":[\"Event\"],\"MediaRecorderErrorEventInit\":[],\"MediaRecorderOptions\":[],\"MediaSession\":[],\"MediaSessionAction\":[],\"MediaSessionActionDetails\":[],\"MediaSessionPlaybackState\":[],\"MediaSource\":[\"EventTarget\"],\"MediaSourceEndOfStreamError\":[],\"MediaSourceEnum\":[],\"MediaSourceReadyState\":[],\"MediaStream\":[\"EventTarget\"],\"MediaStreamAudioDestinationNode\":[\"AudioNode\",\"EventTarget\"],\"MediaStreamAudioSourceNode\":[\"AudioNode\",\"EventTarget\"],\"MediaStreamAudioSourceOptions\":[],\"MediaStreamConstraints\":[],\"MediaStreamError\":[],\"MediaStreamEvent\":[\"Event\"],\"MediaStreamEventInit\":[],\"MediaStreamTrack\":[\"EventTarget\"],\"MediaStreamTrackEvent\":[\"Event\"],\"MediaStreamTrackEventInit\":[],\"MediaStreamTrackGenerator\":[\"EventTarget\",\"MediaStreamTrack\"],\"MediaStreamTrackGeneratorInit\":[],\"MediaStreamTrackProcessor\":[],\"MediaStreamTrackProcessorInit\":[],\"MediaStreamTrackState\":[],\"MediaTrackCapabilities\":[],\"MediaTrackConstraintSet\":[],\"MediaTrackConstraints\":[],\"MediaTrackSettings\":[],\"MediaTrackSupportedConstraints\":[],\"MemoryAttribution\":[],\"MemoryAttributionContainer\":[],\"MemoryBreakdownEntry\":[],\"MemoryMeasurement\":[],\"MessageChannel\":[],\"MessageEvent\":[\"Event\"],\"MessageEventInit\":[],\"MessagePort\":[\"EventTarget\"],\"MidiAccess\":[\"EventTarget\"],\"MidiConnectionEvent\":[\"Event\"],\"MidiConnectionEventInit\":[],\"MidiInput\":[\"EventTarget\",\"MidiPort\"],\"MidiInputMap\":[],\"MidiMessageEvent\":[\"Event\"],\"MidiMessageEventInit\":[],\"MidiOptions\":[],\"MidiOutput\":[\"EventTarget\",\"MidiPort\"],\"MidiOutputMap\":[],\"MidiPort\":[\"EventTarget\"],\"MidiPortConnectionState\":[],\"MidiPortDeviceState\":[],\"MidiPortType\":[],\"MimeType\":[],\"MimeTypeArray\":[],\"MouseEvent\":[\"Event\",\"UiEvent\"],\"MouseEventInit\":[],\"MouseScrollEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"MozDebug\":[],\"MutationEvent\":[\"Event\"],\"MutationObserver\":[],\"MutationObserverInit\":[],\"MutationObservingInfo\":[],\"MutationRecord\":[],\"NamedNodeMap\":[],\"NativeOsFileReadOptions\":[],\"NativeOsFileWriteAtomicOptions\":[],\"NavigationType\":[],\"Navigator\":[],\"NavigatorAutomationInformation\":[],\"NavigatorUaBrandVersion\":[],\"NavigatorUaData\":[],\"NetworkCommandOptions\":[],\"NetworkInformation\":[\"EventTarget\"],\"NetworkResultOptions\":[],\"Node\":[\"EventTarget\"],\"NodeFilter\":[],\"NodeIterator\":[],\"NodeList\":[],\"Notification\":[\"EventTarget\"],\"NotificationAction\":[],\"NotificationDirection\":[],\"NotificationEvent\":[\"Event\",\"ExtendableEvent\"],\"NotificationEventInit\":[],\"NotificationOptions\":[],\"NotificationPermission\":[],\"ObserverCallback\":[],\"OesElementIndexUint\":[],\"OesStandardDerivatives\":[],\"OesTextureFloat\":[],\"OesTextureFloatLinear\":[],\"OesTextureHalfFloat\":[],\"OesTextureHalfFloatLinear\":[],\"OesVertexArrayObject\":[],\"OfflineAudioCompletionEvent\":[\"Event\"],\"OfflineAudioCompletionEventInit\":[],\"OfflineAudioContext\":[\"BaseAudioContext\",\"EventTarget\"],\"OfflineAudioContextOptions\":[],\"OfflineResourceList\":[\"EventTarget\"],\"OffscreenCanvas\":[\"EventTarget\"],\"OffscreenCanvasRenderingContext2d\":[],\"OpenFilePickerOptions\":[],\"OpenWindowEventDetail\":[],\"OptionalEffectTiming\":[],\"OrientationLockType\":[],\"OrientationType\":[],\"OscillatorNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"OscillatorOptions\":[],\"OscillatorType\":[],\"OverSampleType\":[],\"OvrMultiview2\":[],\"PageTransitionEvent\":[\"Event\"],\"PageTransitionEventInit\":[],\"PaintRequest\":[],\"PaintRequestList\":[],\"PaintWorkletGlobalScope\":[\"WorkletGlobalScope\"],\"PannerNode\":[\"AudioNode\",\"EventTarget\"],\"PannerOptions\":[],\"PanningModelType\":[],\"ParityType\":[],\"Path2d\":[],\"PaymentAddress\":[],\"PaymentComplete\":[],\"PaymentMethodChangeEvent\":[\"Event\",\"PaymentRequestUpdateEvent\"],\"PaymentMethodChangeEventInit\":[],\"PaymentRequestUpdateEvent\":[\"Event\"],\"PaymentRequestUpdateEventInit\":[],\"PaymentResponse\":[],\"Pbkdf2Params\":[],\"PcImplIceConnectionState\":[],\"PcImplIceGatheringState\":[],\"PcImplSignalingState\":[],\"PcObserverStateType\":[],\"Performance\":[\"EventTarget\"],\"PerformanceEntry\":[],\"PerformanceEntryEventInit\":[],\"PerformanceEntryFilterOptions\":[],\"PerformanceMark\":[\"PerformanceEntry\"],\"PerformanceMeasure\":[\"PerformanceEntry\"],\"PerformanceNavigation\":[],\"PerformanceNavigationTiming\":[\"PerformanceEntry\",\"PerformanceResourceTiming\"],\"PerformanceObserver\":[],\"PerformanceObserverEntryList\":[],\"PerformanceObserverInit\":[],\"PerformanceResourceTiming\":[\"PerformanceEntry\"],\"PerformanceServerTiming\":[],\"PerformanceTiming\":[],\"PeriodicWave\":[],\"PeriodicWaveConstraints\":[],\"PeriodicWaveOptions\":[],\"PermissionDescriptor\":[],\"PermissionName\":[],\"PermissionState\":[],\"PermissionStatus\":[\"EventTarget\"],\"Permissions\":[],\"PlaneLayout\":[],\"PlaybackDirection\":[],\"Plugin\":[],\"PluginArray\":[],\"PluginCrashedEventInit\":[],\"PointerEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"PointerEventInit\":[],\"PopStateEvent\":[\"Event\"],\"PopStateEventInit\":[],\"PopupBlockedEvent\":[\"Event\"],\"PopupBlockedEventInit\":[],\"Position\":[],\"PositionAlignSetting\":[],\"PositionError\":[],\"PositionOptions\":[],\"PremultiplyAlpha\":[],\"Presentation\":[],\"PresentationAvailability\":[\"EventTarget\"],\"PresentationConnection\":[\"EventTarget\"],\"PresentationConnectionAvailableEvent\":[\"Event\"],\"PresentationConnectionAvailableEventInit\":[],\"PresentationConnectionBinaryType\":[],\"PresentationConnectionCloseEvent\":[\"Event\"],\"PresentationConnectionCloseEventInit\":[],\"PresentationConnectionClosedReason\":[],\"PresentationConnectionList\":[\"EventTarget\"],\"PresentationConnectionState\":[],\"PresentationReceiver\":[],\"PresentationRequest\":[\"EventTarget\"],\"PresentationStyle\":[],\"ProcessingInstruction\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"ProfileTimelineLayerRect\":[],\"ProfileTimelineMarker\":[],\"ProfileTimelineMessagePortOperationType\":[],\"ProfileTimelineStackFrame\":[],\"ProfileTimelineWorkerOperationType\":[],\"ProgressEvent\":[\"Event\"],\"ProgressEventInit\":[],\"PromiseNativeHandler\":[],\"PromiseRejectionEvent\":[\"Event\"],\"PromiseRejectionEventInit\":[],\"PublicKeyCredential\":[\"Credential\"],\"PublicKeyCredentialCreationOptions\":[],\"PublicKeyCredentialCreationOptionsJson\":[],\"PublicKeyCredentialDescriptor\":[],\"PublicKeyCredentialDescriptorJson\":[],\"PublicKeyCredentialEntity\":[],\"PublicKeyCredentialHints\":[],\"PublicKeyCredentialParameters\":[],\"PublicKeyCredentialRequestOptions\":[],\"PublicKeyCredentialRequestOptionsJson\":[],\"PublicKeyCredentialRpEntity\":[],\"PublicKeyCredentialType\":[],\"PublicKeyCredentialUserEntity\":[],\"PublicKeyCredentialUserEntityJson\":[],\"PushEncryptionKeyName\":[],\"PushEvent\":[\"Event\",\"ExtendableEvent\"],\"PushEventInit\":[],\"PushManager\":[],\"PushMessageData\":[],\"PushPermissionState\":[],\"PushSubscription\":[],\"PushSubscriptionInit\":[],\"PushSubscriptionJson\":[],\"PushSubscriptionKeys\":[],\"PushSubscriptionOptions\":[],\"PushSubscriptionOptionsInit\":[],\"QueryOptions\":[],\"QueuingStrategy\":[],\"QueuingStrategyInit\":[],\"RadioNodeList\":[\"NodeList\"],\"Range\":[],\"RcwnPerfStats\":[],\"RcwnStatus\":[],\"ReadableByteStreamController\":[],\"ReadableStream\":[],\"ReadableStreamByobReader\":[],\"ReadableStreamByobRequest\":[],\"ReadableStreamDefaultController\":[],\"ReadableStreamDefaultReader\":[],\"ReadableStreamGetReaderOptions\":[],\"ReadableStreamIteratorOptions\":[],\"ReadableStreamReadResult\":[],\"ReadableStreamReaderMode\":[],\"ReadableStreamType\":[],\"ReadableWritablePair\":[],\"RecordingState\":[],\"ReferrerPolicy\":[],\"RegisterRequest\":[],\"RegisterResponse\":[],\"RegisteredKey\":[],\"RegistrationOptions\":[],\"RegistrationResponseJson\":[],\"Request\":[],\"RequestCache\":[],\"RequestCredentials\":[],\"RequestDestination\":[],\"RequestDeviceOptions\":[],\"RequestInit\":[],\"RequestMediaKeySystemAccessNotification\":[],\"RequestMode\":[],\"RequestRedirect\":[],\"ResidentKeyRequirement\":[],\"ResizeObserver\":[],\"ResizeObserverBoxOptions\":[],\"ResizeObserverEntry\":[],\"ResizeObserverOptions\":[],\"ResizeObserverSize\":[],\"ResizeQuality\":[],\"Response\":[],\"ResponseInit\":[],\"ResponseType\":[],\"RsaHashedImportParams\":[],\"RsaOaepParams\":[],\"RsaOtherPrimesInfo\":[],\"RsaPssParams\":[],\"RtcAnswerOptions\":[],\"RtcBundlePolicy\":[],\"RtcCertificate\":[],\"RtcCertificateExpiration\":[],\"RtcCodecStats\":[],\"RtcConfiguration\":[],\"RtcDataChannel\":[\"EventTarget\"],\"RtcDataChannelEvent\":[\"Event\"],\"RtcDataChannelEventInit\":[],\"RtcDataChannelInit\":[],\"RtcDataChannelState\":[],\"RtcDataChannelType\":[],\"RtcDegradationPreference\":[],\"RtcEncodedAudioFrame\":[],\"RtcEncodedAudioFrameMetadata\":[],\"RtcEncodedAudioFrameOptions\":[],\"RtcEncodedVideoFrame\":[],\"RtcEncodedVideoFrameMetadata\":[],\"RtcEncodedVideoFrameOptions\":[],\"RtcEncodedVideoFrameType\":[],\"RtcFecParameters\":[],\"RtcIceCandidate\":[],\"RtcIceCandidateInit\":[],\"RtcIceCandidatePairStats\":[],\"RtcIceCandidateStats\":[],\"RtcIceComponentStats\":[],\"RtcIceConnectionState\":[],\"RtcIceCredentialType\":[],\"RtcIceGatheringState\":[],\"RtcIceServer\":[],\"RtcIceTransportPolicy\":[],\"RtcIdentityAssertion\":[],\"RtcIdentityAssertionResult\":[],\"RtcIdentityProvider\":[],\"RtcIdentityProviderDetails\":[],\"RtcIdentityProviderOptions\":[],\"RtcIdentityProviderRegistrar\":[],\"RtcIdentityValidationResult\":[],\"RtcInboundRtpStreamStats\":[],\"RtcMediaStreamStats\":[],\"RtcMediaStreamTrackStats\":[],\"RtcOfferAnswerOptions\":[],\"RtcOfferOptions\":[],\"RtcOutboundRtpStreamStats\":[],\"RtcPeerConnection\":[\"EventTarget\"],\"RtcPeerConnectionIceErrorEvent\":[\"Event\"],\"RtcPeerConnectionIceEvent\":[\"Event\"],\"RtcPeerConnectionIceEventInit\":[],\"RtcPeerConnectionState\":[],\"RtcPriorityType\":[],\"RtcRtcpParameters\":[],\"RtcRtpCapabilities\":[],\"RtcRtpCodecCapability\":[],\"RtcRtpCodecParameters\":[],\"RtcRtpContributingSource\":[],\"RtcRtpEncodingParameters\":[],\"RtcRtpHeaderExtensionCapability\":[],\"RtcRtpHeaderExtensionParameters\":[],\"RtcRtpParameters\":[],\"RtcRtpReceiver\":[],\"RtcRtpScriptTransform\":[],\"RtcRtpScriptTransformer\":[\"EventTarget\"],\"RtcRtpSender\":[],\"RtcRtpSourceEntry\":[],\"RtcRtpSourceEntryType\":[],\"RtcRtpSynchronizationSource\":[],\"RtcRtpTransceiver\":[],\"RtcRtpTransceiverDirection\":[],\"RtcRtpTransceiverInit\":[],\"RtcRtxParameters\":[],\"RtcSdpType\":[],\"RtcSessionDescription\":[],\"RtcSessionDescriptionInit\":[],\"RtcSignalingState\":[],\"RtcStats\":[],\"RtcStatsIceCandidatePairState\":[],\"RtcStatsIceCandidateType\":[],\"RtcStatsReport\":[],\"RtcStatsReportInternal\":[],\"RtcStatsType\":[],\"RtcTrackEvent\":[\"Event\"],\"RtcTrackEventInit\":[],\"RtcTransformEvent\":[\"Event\"],\"RtcTransportStats\":[],\"RtcdtmfSender\":[\"EventTarget\"],\"RtcdtmfToneChangeEvent\":[\"Event\"],\"RtcdtmfToneChangeEventInit\":[],\"RtcrtpContributingSourceStats\":[],\"RtcrtpStreamStats\":[],\"SFrameTransform\":[\"EventTarget\"],\"SFrameTransformErrorEvent\":[\"Event\"],\"SFrameTransformErrorEventInit\":[],\"SFrameTransformErrorEventType\":[],\"SFrameTransformOptions\":[],\"SFrameTransformRole\":[],\"SaveFilePickerOptions\":[],\"Scheduler\":[],\"SchedulerPostTaskOptions\":[],\"Scheduling\":[],\"Screen\":[\"EventTarget\"],\"ScreenColorGamut\":[],\"ScreenLuminance\":[],\"ScreenOrientation\":[\"EventTarget\"],\"ScriptProcessorNode\":[\"AudioNode\",\"EventTarget\"],\"ScrollAreaEvent\":[\"Event\",\"UiEvent\"],\"ScrollBehavior\":[],\"ScrollBoxObject\":[],\"ScrollIntoViewOptions\":[],\"ScrollLogicalPosition\":[],\"ScrollOptions\":[],\"ScrollRestoration\":[],\"ScrollSetting\":[],\"ScrollState\":[],\"ScrollToOptions\":[],\"ScrollViewChangeEventInit\":[],\"SecurityPolicyViolationEvent\":[\"Event\"],\"SecurityPolicyViolationEventDisposition\":[],\"SecurityPolicyViolationEventInit\":[],\"Selection\":[],\"SelectionMode\":[],\"Serial\":[\"EventTarget\"],\"SerialInputSignals\":[],\"SerialOptions\":[],\"SerialOutputSignals\":[],\"SerialPort\":[\"EventTarget\"],\"SerialPortFilter\":[],\"SerialPortInfo\":[],\"SerialPortRequestOptions\":[],\"ServerSocketOptions\":[],\"ServiceWorker\":[\"EventTarget\"],\"ServiceWorkerContainer\":[\"EventTarget\"],\"ServiceWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"ServiceWorkerRegistration\":[\"EventTarget\"],\"ServiceWorkerState\":[],\"ServiceWorkerUpdateViaCache\":[],\"ShadowRoot\":[\"DocumentFragment\",\"EventTarget\",\"Node\"],\"ShadowRootInit\":[],\"ShadowRootMode\":[],\"ShareData\":[],\"SharedWorker\":[\"EventTarget\"],\"SharedWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"SignResponse\":[],\"SocketElement\":[],\"SocketOptions\":[],\"SocketReadyState\":[],\"SocketsDict\":[],\"SourceBuffer\":[\"EventTarget\"],\"SourceBufferAppendMode\":[],\"SourceBufferList\":[\"EventTarget\"],\"SpeechGrammar\":[],\"SpeechGrammarList\":[],\"SpeechRecognition\":[\"EventTarget\"],\"SpeechRecognitionAlternative\":[],\"SpeechRecognitionError\":[\"Event\"],\"SpeechRecognitionErrorCode\":[],\"SpeechRecognitionErrorInit\":[],\"SpeechRecognitionEvent\":[\"Event\"],\"SpeechRecognitionEventInit\":[],\"SpeechRecognitionResult\":[],\"SpeechRecognitionResultList\":[],\"SpeechSynthesis\":[\"EventTarget\"],\"SpeechSynthesisErrorCode\":[],\"SpeechSynthesisErrorEvent\":[\"Event\",\"SpeechSynthesisEvent\"],\"SpeechSynthesisErrorEventInit\":[],\"SpeechSynthesisEvent\":[\"Event\"],\"SpeechSynthesisEventInit\":[],\"SpeechSynthesisUtterance\":[\"EventTarget\"],\"SpeechSynthesisVoice\":[],\"StereoPannerNode\":[\"AudioNode\",\"EventTarget\"],\"StereoPannerOptions\":[],\"Storage\":[],\"StorageEstimate\":[],\"StorageEvent\":[\"Event\"],\"StorageEventInit\":[],\"StorageManager\":[],\"StorageType\":[],\"StreamPipeOptions\":[],\"StyleRuleChangeEventInit\":[],\"StyleSheet\":[],\"StyleSheetApplicableStateChangeEventInit\":[],\"StyleSheetChangeEventInit\":[],\"StyleSheetList\":[],\"SubmitEvent\":[\"Event\"],\"SubmitEventInit\":[],\"SubtleCrypto\":[],\"SupportedType\":[],\"SvcOutputMetadata\":[],\"SvgAngle\":[],\"SvgAnimateElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimateMotionElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimateTransformElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimatedAngle\":[],\"SvgAnimatedBoolean\":[],\"SvgAnimatedEnumeration\":[],\"SvgAnimatedInteger\":[],\"SvgAnimatedLength\":[],\"SvgAnimatedLengthList\":[],\"SvgAnimatedNumber\":[],\"SvgAnimatedNumberList\":[],\"SvgAnimatedPreserveAspectRatio\":[],\"SvgAnimatedRect\":[],\"SvgAnimatedString\":[],\"SvgAnimatedTransformList\":[],\"SvgAnimationElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgBoundingBoxOptions\":[],\"SvgCircleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgClipPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgComponentTransferFunctionElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgDefsElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgDescElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgElement\":[\"Element\",\"EventTarget\",\"Node\"],\"SvgEllipseElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgFilterElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgForeignObjectElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgGeometryElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgGraphicsElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgImageElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgLength\":[],\"SvgLengthList\":[],\"SvgLineElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgLinearGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGradientElement\"],\"SvgMarkerElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgMaskElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgMatrix\":[],\"SvgMetadataElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgNumber\":[],\"SvgNumberList\":[],\"SvgPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPathSeg\":[],\"SvgPathSegArcAbs\":[\"SvgPathSeg\"],\"SvgPathSegArcRel\":[\"SvgPathSeg\"],\"SvgPathSegClosePath\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicSmoothAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicSmoothRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticSmoothAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticSmoothRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoHorizontalAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoHorizontalRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoVerticalAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoVerticalRel\":[\"SvgPathSeg\"],\"SvgPathSegList\":[],\"SvgPathSegMovetoAbs\":[\"SvgPathSeg\"],\"SvgPathSegMovetoRel\":[\"SvgPathSeg\"],\"SvgPatternElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgPoint\":[],\"SvgPointList\":[],\"SvgPolygonElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPolylineElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPreserveAspectRatio\":[],\"SvgRadialGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGradientElement\"],\"SvgRect\":[],\"SvgRectElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgScriptElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgSetElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgStopElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgStringList\":[],\"SvgStyleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgSwitchElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgSymbolElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgTextContentElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgTextElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\",\"SvgTextPositioningElement\"],\"SvgTextPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\"],\"SvgTextPositioningElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\"],\"SvgTitleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgTransform\":[],\"SvgTransformList\":[],\"SvgUnitTypes\":[],\"SvgUseElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgViewElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgZoomAndPan\":[],\"SvgaElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgfeBlendElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeColorMatrixElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeComponentTransferElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeCompositeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeConvolveMatrixElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDiffuseLightingElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDisplacementMapElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDistantLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDropShadowElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeFloodElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeFuncAElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncBElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncGElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncRElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeGaussianBlurElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeImageElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMergeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMergeNodeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMorphologyElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeOffsetElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfePointLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeSpecularLightingElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeSpotLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeTileElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeTurbulenceElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvggElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgmPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgsvgElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgtSpanElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\",\"SvgTextPositioningElement\"],\"TaskController\":[\"AbortController\"],\"TaskControllerInit\":[],\"TaskPriority\":[],\"TaskPriorityChangeEvent\":[\"Event\"],\"TaskPriorityChangeEventInit\":[],\"TaskSignal\":[\"AbortSignal\",\"EventTarget\"],\"TaskSignalAnyInit\":[],\"TcpReadyState\":[],\"TcpServerSocket\":[\"EventTarget\"],\"TcpServerSocketEvent\":[\"Event\"],\"TcpServerSocketEventInit\":[],\"TcpSocket\":[\"EventTarget\"],\"TcpSocketBinaryType\":[],\"TcpSocketErrorEvent\":[\"Event\"],\"TcpSocketErrorEventInit\":[],\"TcpSocketEvent\":[\"Event\"],\"TcpSocketEventInit\":[],\"Text\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"TextDecodeOptions\":[],\"TextDecoder\":[],\"TextDecoderOptions\":[],\"TextEncoder\":[],\"TextMetrics\":[],\"TextTrack\":[\"EventTarget\"],\"TextTrackCue\":[\"EventTarget\"],\"TextTrackCueList\":[],\"TextTrackKind\":[],\"TextTrackList\":[\"EventTarget\"],\"TextTrackMode\":[],\"TimeEvent\":[\"Event\"],\"TimeRanges\":[],\"ToggleEvent\":[\"Event\"],\"ToggleEventInit\":[],\"TokenBinding\":[],\"TokenBindingStatus\":[],\"Touch\":[],\"TouchEvent\":[\"Event\",\"UiEvent\"],\"TouchEventInit\":[],\"TouchInit\":[],\"TouchList\":[],\"TrackEvent\":[\"Event\"],\"TrackEventInit\":[],\"TransformStream\":[],\"TransformStreamDefaultController\":[],\"Transformer\":[],\"TransitionEvent\":[\"Event\"],\"TransitionEventInit\":[],\"Transport\":[],\"TreeBoxObject\":[],\"TreeCellInfo\":[],\"TreeView\":[],\"TreeWalker\":[],\"U2f\":[],\"U2fClientData\":[],\"ULongRange\":[],\"UaDataValues\":[],\"UaLowEntropyJson\":[],\"UdpMessageEventInit\":[],\"UdpOptions\":[],\"UiEvent\":[\"Event\"],\"UiEventInit\":[],\"UnderlyingSink\":[],\"UnderlyingSource\":[],\"Url\":[],\"UrlSearchParams\":[],\"Usb\":[\"EventTarget\"],\"UsbAlternateInterface\":[],\"UsbConfiguration\":[],\"UsbConnectionEvent\":[\"Event\"],\"UsbConnectionEventInit\":[],\"UsbControlTransferParameters\":[],\"UsbDevice\":[],\"UsbDeviceFilter\":[],\"UsbDeviceRequestOptions\":[],\"UsbDirection\":[],\"UsbEndpoint\":[],\"UsbEndpointType\":[],\"UsbInTransferResult\":[],\"UsbInterface\":[],\"UsbIsochronousInTransferPacket\":[],\"UsbIsochronousInTransferResult\":[],\"UsbIsochronousOutTransferPacket\":[],\"UsbIsochronousOutTransferResult\":[],\"UsbOutTransferResult\":[],\"UsbPermissionDescriptor\":[],\"UsbPermissionResult\":[\"EventTarget\",\"PermissionStatus\"],\"UsbPermissionStorage\":[],\"UsbRecipient\":[],\"UsbRequestType\":[],\"UsbTransferStatus\":[],\"UserActivation\":[],\"UserProximityEvent\":[\"Event\"],\"UserProximityEventInit\":[],\"UserVerificationRequirement\":[],\"ValidityState\":[],\"ValueEvent\":[\"Event\"],\"ValueEventInit\":[],\"VideoColorPrimaries\":[],\"VideoColorSpace\":[],\"VideoColorSpaceInit\":[],\"VideoConfiguration\":[],\"VideoDecoder\":[],\"VideoDecoderConfig\":[],\"VideoDecoderInit\":[],\"VideoDecoderSupport\":[],\"VideoEncoder\":[],\"VideoEncoderConfig\":[],\"VideoEncoderEncodeOptions\":[],\"VideoEncoderInit\":[],\"VideoEncoderSupport\":[],\"VideoFacingModeEnum\":[],\"VideoFrame\":[],\"VideoFrameBufferInit\":[],\"VideoFrameCopyToOptions\":[],\"VideoFrameInit\":[],\"VideoMatrixCoefficients\":[],\"VideoPixelFormat\":[],\"VideoPlaybackQuality\":[],\"VideoStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"VideoTrack\":[],\"VideoTrackList\":[\"EventTarget\"],\"VideoTransferCharacteristics\":[],\"ViewTransition\":[],\"VisibilityState\":[],\"VisualViewport\":[\"EventTarget\"],\"VoidCallback\":[],\"VrDisplay\":[\"EventTarget\"],\"VrDisplayCapabilities\":[],\"VrEye\":[],\"VrEyeParameters\":[],\"VrFieldOfView\":[],\"VrFrameData\":[],\"VrLayer\":[],\"VrMockController\":[],\"VrMockDisplay\":[],\"VrPose\":[],\"VrServiceTest\":[],\"VrStageParameters\":[],\"VrSubmitFrameResult\":[],\"VttCue\":[\"EventTarget\",\"TextTrackCue\"],\"VttRegion\":[],\"WakeLock\":[],\"WakeLockSentinel\":[\"EventTarget\"],\"WakeLockType\":[],\"WatchAdvertisementsOptions\":[],\"WaveShaperNode\":[\"AudioNode\",\"EventTarget\"],\"WaveShaperOptions\":[],\"WebGl2RenderingContext\":[],\"WebGlActiveInfo\":[],\"WebGlBuffer\":[],\"WebGlContextAttributes\":[],\"WebGlContextEvent\":[\"Event\"],\"WebGlContextEventInit\":[],\"WebGlFramebuffer\":[],\"WebGlPowerPreference\":[],\"WebGlProgram\":[],\"WebGlQuery\":[],\"WebGlRenderbuffer\":[],\"WebGlRenderingContext\":[],\"WebGlSampler\":[],\"WebGlShader\":[],\"WebGlShaderPrecisionFormat\":[],\"WebGlSync\":[],\"WebGlTexture\":[],\"WebGlTransformFeedback\":[],\"WebGlUniformLocation\":[],\"WebGlVertexArrayObject\":[],\"WebKitCssMatrix\":[\"DomMatrix\",\"DomMatrixReadOnly\"],\"WebSocket\":[\"EventTarget\"],\"WebSocketDict\":[],\"WebSocketElement\":[],\"WebTransport\":[],\"WebTransportBidirectionalStream\":[],\"WebTransportCloseInfo\":[],\"WebTransportCongestionControl\":[],\"WebTransportDatagramDuplexStream\":[],\"WebTransportDatagramStats\":[],\"WebTransportError\":[\"DomException\"],\"WebTransportErrorOptions\":[],\"WebTransportErrorSource\":[],\"WebTransportHash\":[],\"WebTransportOptions\":[],\"WebTransportReceiveStream\":[\"ReadableStream\"],\"WebTransportReceiveStreamStats\":[],\"WebTransportReliabilityMode\":[],\"WebTransportSendStream\":[\"WritableStream\"],\"WebTransportSendStreamOptions\":[],\"WebTransportSendStreamStats\":[],\"WebTransportStats\":[],\"WebglColorBufferFloat\":[],\"WebglCompressedTextureAstc\":[],\"WebglCompressedTextureAtc\":[],\"WebglCompressedTextureEtc\":[],\"WebglCompressedTextureEtc1\":[],\"WebglCompressedTexturePvrtc\":[],\"WebglCompressedTextureS3tc\":[],\"WebglCompressedTextureS3tcSrgb\":[],\"WebglDebugRendererInfo\":[],\"WebglDebugShaders\":[],\"WebglDepthTexture\":[],\"WebglDrawBuffers\":[],\"WebglLoseContext\":[],\"WebglMultiDraw\":[],\"WellKnownDirectory\":[],\"WgslLanguageFeatures\":[],\"WheelEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"WheelEventInit\":[],\"WidevineCdmManifest\":[],\"Window\":[\"EventTarget\"],\"WindowClient\":[\"Client\"],\"Worker\":[\"EventTarget\"],\"WorkerDebuggerGlobalScope\":[\"EventTarget\"],\"WorkerGlobalScope\":[\"EventTarget\"],\"WorkerLocation\":[],\"WorkerNavigator\":[],\"WorkerOptions\":[],\"WorkerType\":[],\"Worklet\":[],\"WorkletGlobalScope\":[],\"WorkletOptions\":[],\"WritableStream\":[],\"WritableStreamDefaultController\":[],\"WritableStreamDefaultWriter\":[],\"WriteCommandType\":[],\"WriteParams\":[],\"XPathExpression\":[],\"XPathNsResolver\":[],\"XPathResult\":[],\"XmlDocument\":[\"Document\",\"EventTarget\",\"Node\"],\"XmlHttpRequest\":[\"EventTarget\",\"XmlHttpRequestEventTarget\"],\"XmlHttpRequestEventTarget\":[\"EventTarget\"],\"XmlHttpRequestResponseType\":[],\"XmlHttpRequestUpload\":[\"EventTarget\",\"XmlHttpRequestEventTarget\"],\"XmlSerializer\":[],\"XrBoundedReferenceSpace\":[\"EventTarget\",\"XrReferenceSpace\",\"XrSpace\"],\"XrEye\":[],\"XrFrame\":[],\"XrHand\":[],\"XrHandJoint\":[],\"XrHandedness\":[],\"XrInputSource\":[],\"XrInputSourceArray\":[],\"XrInputSourceEvent\":[\"Event\"],\"XrInputSourceEventInit\":[],\"XrInputSourcesChangeEvent\":[\"Event\"],\"XrInputSourcesChangeEventInit\":[],\"XrJointPose\":[\"XrPose\"],\"XrJointSpace\":[\"EventTarget\",\"XrSpace\"],\"XrLayer\":[\"EventTarget\"],\"XrPermissionDescriptor\":[],\"XrPermissionStatus\":[\"EventTarget\",\"PermissionStatus\"],\"XrPose\":[],\"XrReferenceSpace\":[\"EventTarget\",\"XrSpace\"],\"XrReferenceSpaceEvent\":[\"Event\"],\"XrReferenceSpaceEventInit\":[],\"XrReferenceSpaceType\":[],\"XrRenderState\":[],\"XrRenderStateInit\":[],\"XrRigidTransform\":[],\"XrSession\":[\"EventTarget\"],\"XrSessionEvent\":[\"Event\"],\"XrSessionEventInit\":[],\"XrSessionInit\":[],\"XrSessionMode\":[],\"XrSessionSupportedPermissionDescriptor\":[],\"XrSpace\":[\"EventTarget\"],\"XrSystem\":[\"EventTarget\"],\"XrTargetRayMode\":[],\"XrView\":[],\"XrViewerPose\":[\"XrPose\"],\"XrViewport\":[],\"XrVisibilityState\":[],\"XrWebGlLayer\":[\"EventTarget\",\"XrLayer\"],\"XrWebGlLayerInit\":[],\"XsltProcessor\":[],\"console\":[],\"css\":[],\"default\":[\"std\"],\"gpu_buffer_usage\":[],\"gpu_color_write\":[],\"gpu_map_mode\":[],\"gpu_shader_stage\":[],\"gpu_texture_usage\":[],\"std\":[\"wasm-bindgen/std\",\"js-sys/std\"]}}", + "wayland-backend_0.3.12": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"concat-idents\",\"req\":\"^1.1\"},{\"name\":\"downcast-rs\",\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.10\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"raw-window-handle\",\"optional\":true,\"req\":\"^0.5.0\"},{\"features\":[\"event\",\"fs\",\"net\",\"process\"],\"name\":\"rustix\",\"req\":\"^1.0.2\"},{\"name\":\"rwh_06\",\"optional\":true,\"package\":\"raw-window-handle\",\"req\":\"^0.6.0\"},{\"name\":\"scoped-tls\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"scoped-tls\",\"req\":\"^1.0\"},{\"features\":[\"union\",\"const_generics\",\"const_new\"],\"name\":\"smallvec\",\"req\":\"^1.9\"},{\"name\":\"wayland-sys\",\"req\":\"^0.31.8\"}],\"features\":{\"client_system\":[\"wayland-sys/client\",\"dep:scoped-tls\"],\"dlopen\":[\"wayland-sys/dlopen\"],\"server_system\":[\"wayland-sys/server\",\"dep:scoped-tls\"]}}", + "wayland-client_0.31.12": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3.16\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"features\":[\"event\"],\"name\":\"rustix\",\"req\":\"^1.0.2\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.2\"},{\"name\":\"wayland-backend\",\"req\":\"^0.3.12\"},{\"name\":\"wayland-scanner\",\"req\":\"^0.31.8\"}],\"features\":{}}", + "wayland-protocols-wlr_0.3.10": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"name\":\"wayland-backend\",\"req\":\"^0.3.12\"},{\"name\":\"wayland-client\",\"optional\":true,\"req\":\"^0.31.12\"},{\"name\":\"wayland-protocols\",\"req\":\"^0.32.10\"},{\"name\":\"wayland-scanner\",\"req\":\"^0.31.8\"},{\"name\":\"wayland-server\",\"optional\":true,\"req\":\"^0.31.11\"}],\"features\":{\"client\":[\"wayland-client\",\"wayland-protocols/client\"],\"server\":[\"wayland-server\",\"wayland-protocols/server\"]}}", + "wayland-protocols_0.32.10": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2\"},{\"name\":\"wayland-backend\",\"req\":\"^0.3.12\"},{\"name\":\"wayland-client\",\"optional\":true,\"req\":\"^0.31.12\"},{\"name\":\"wayland-scanner\",\"req\":\"^0.31.8\"},{\"name\":\"wayland-server\",\"optional\":true,\"req\":\"^0.31.11\"}],\"features\":{\"client\":[\"wayland-client\"],\"server\":[\"wayland-server\"],\"staging\":[],\"unstable\":[]}}", + "wayland-scanner_0.31.8": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.11\"},{\"name\":\"quick-xml\",\"req\":\"^0.38.3\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"similar\",\"req\":\"^2\"}],\"features\":{}}", + "wayland-sys_0.31.8": "{\"dependencies\":[{\"name\":\"dlib\",\"optional\":true,\"req\":\"^0.5.1\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"memoffset\",\"optional\":true,\"req\":\"^0.9\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.7\"}],\"features\":{\"client\":[\"dep:dlib\",\"dep:log\"],\"cursor\":[\"client\"],\"dlopen\":[\"once_cell\"],\"egl\":[\"client\"],\"server\":[\"libc\",\"memoffset\",\"dep:dlib\",\"dep:log\"]}}", + "web-sys_0.3.85": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"default_features\":false,\"name\":\"js-sys\",\"req\":\"=0.3.85\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.108\"}],\"features\":{\"AbortController\":[],\"AbortSignal\":[\"EventTarget\"],\"AddEventListenerOptions\":[],\"AesCbcParams\":[],\"AesCtrParams\":[],\"AesDerivedKeyParams\":[],\"AesGcmParams\":[],\"AesKeyAlgorithm\":[],\"AesKeyGenParams\":[],\"Algorithm\":[],\"AlignSetting\":[],\"AllowedBluetoothDevice\":[],\"AllowedUsbDevice\":[],\"AlphaOption\":[],\"AnalyserNode\":[\"AudioNode\",\"EventTarget\"],\"AnalyserOptions\":[],\"AngleInstancedArrays\":[],\"Animation\":[\"EventTarget\"],\"AnimationEffect\":[],\"AnimationEvent\":[\"Event\"],\"AnimationEventInit\":[],\"AnimationPlayState\":[],\"AnimationPlaybackEvent\":[\"Event\"],\"AnimationPlaybackEventInit\":[],\"AnimationPropertyDetails\":[],\"AnimationPropertyValueDetails\":[],\"AnimationTimeline\":[],\"AssignedNodesOptions\":[],\"AttestationConveyancePreference\":[],\"Attr\":[\"EventTarget\",\"Node\"],\"AttributeNameValue\":[],\"AudioBuffer\":[],\"AudioBufferOptions\":[],\"AudioBufferSourceNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"AudioBufferSourceOptions\":[],\"AudioConfiguration\":[],\"AudioContext\":[\"BaseAudioContext\",\"EventTarget\"],\"AudioContextLatencyCategory\":[],\"AudioContextOptions\":[],\"AudioContextState\":[],\"AudioData\":[],\"AudioDataCopyToOptions\":[],\"AudioDataInit\":[],\"AudioDecoder\":[],\"AudioDecoderConfig\":[],\"AudioDecoderInit\":[],\"AudioDecoderSupport\":[],\"AudioDestinationNode\":[\"AudioNode\",\"EventTarget\"],\"AudioEncoder\":[],\"AudioEncoderConfig\":[],\"AudioEncoderInit\":[],\"AudioEncoderSupport\":[],\"AudioListener\":[],\"AudioNode\":[\"EventTarget\"],\"AudioNodeOptions\":[],\"AudioParam\":[],\"AudioParamMap\":[],\"AudioProcessingEvent\":[\"Event\"],\"AudioSampleFormat\":[],\"AudioScheduledSourceNode\":[\"AudioNode\",\"EventTarget\"],\"AudioSinkInfo\":[],\"AudioSinkOptions\":[],\"AudioSinkType\":[],\"AudioStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"AudioTrack\":[],\"AudioTrackList\":[\"EventTarget\"],\"AudioWorklet\":[\"Worklet\"],\"AudioWorkletGlobalScope\":[\"WorkletGlobalScope\"],\"AudioWorkletNode\":[\"AudioNode\",\"EventTarget\"],\"AudioWorkletNodeOptions\":[],\"AudioWorkletProcessor\":[],\"AuthenticationExtensionsClientInputs\":[],\"AuthenticationExtensionsClientInputsJson\":[],\"AuthenticationExtensionsClientOutputs\":[],\"AuthenticationExtensionsClientOutputsJson\":[],\"AuthenticationExtensionsDevicePublicKeyInputs\":[],\"AuthenticationExtensionsDevicePublicKeyOutputs\":[],\"AuthenticationExtensionsLargeBlobInputs\":[],\"AuthenticationExtensionsLargeBlobOutputs\":[],\"AuthenticationExtensionsPrfInputs\":[],\"AuthenticationExtensionsPrfOutputs\":[],\"AuthenticationExtensionsPrfValues\":[],\"AuthenticationResponseJson\":[],\"AuthenticatorAssertionResponse\":[\"AuthenticatorResponse\"],\"AuthenticatorAssertionResponseJson\":[],\"AuthenticatorAttachment\":[],\"AuthenticatorAttestationResponse\":[\"AuthenticatorResponse\"],\"AuthenticatorAttestationResponseJson\":[],\"AuthenticatorResponse\":[],\"AuthenticatorSelectionCriteria\":[],\"AuthenticatorTransport\":[],\"AutoKeyword\":[],\"AutocompleteInfo\":[],\"BarProp\":[],\"BaseAudioContext\":[\"EventTarget\"],\"BaseComputedKeyframe\":[],\"BaseKeyframe\":[],\"BasePropertyIndexedKeyframe\":[],\"BasicCardRequest\":[],\"BasicCardResponse\":[],\"BasicCardType\":[],\"BatteryManager\":[\"EventTarget\"],\"BeforeUnloadEvent\":[\"Event\"],\"BinaryType\":[],\"BiquadFilterNode\":[\"AudioNode\",\"EventTarget\"],\"BiquadFilterOptions\":[],\"BiquadFilterType\":[],\"Blob\":[],\"BlobEvent\":[\"Event\"],\"BlobEventInit\":[],\"BlobPropertyBag\":[],\"BlockParsingOptions\":[],\"Bluetooth\":[\"EventTarget\"],\"BluetoothAdvertisingEvent\":[\"Event\"],\"BluetoothAdvertisingEventInit\":[],\"BluetoothCharacteristicProperties\":[],\"BluetoothDataFilterInit\":[],\"BluetoothDevice\":[\"EventTarget\"],\"BluetoothLeScanFilterInit\":[],\"BluetoothManufacturerDataMap\":[],\"BluetoothPermissionDescriptor\":[],\"BluetoothPermissionResult\":[\"EventTarget\",\"PermissionStatus\"],\"BluetoothPermissionStorage\":[],\"BluetoothRemoteGattCharacteristic\":[\"EventTarget\"],\"BluetoothRemoteGattDescriptor\":[],\"BluetoothRemoteGattServer\":[],\"BluetoothRemoteGattService\":[\"EventTarget\"],\"BluetoothServiceDataMap\":[],\"BluetoothUuid\":[],\"BoxQuadOptions\":[],\"BroadcastChannel\":[\"EventTarget\"],\"BrowserElementDownloadOptions\":[],\"BrowserElementExecuteScriptOptions\":[],\"BrowserFeedWriter\":[],\"BrowserFindCaseSensitivity\":[],\"BrowserFindDirection\":[],\"ByteLengthQueuingStrategy\":[],\"Cache\":[],\"CacheBatchOperation\":[],\"CacheQueryOptions\":[],\"CacheStorage\":[],\"CacheStorageNamespace\":[],\"CanvasCaptureMediaStream\":[\"EventTarget\",\"MediaStream\"],\"CanvasCaptureMediaStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"CanvasGradient\":[],\"CanvasPattern\":[],\"CanvasRenderingContext2d\":[],\"CanvasWindingRule\":[],\"CaretChangedReason\":[],\"CaretPosition\":[],\"CaretStateChangedEventInit\":[],\"CdataSection\":[\"CharacterData\",\"EventTarget\",\"Node\",\"Text\"],\"ChannelCountMode\":[],\"ChannelInterpretation\":[],\"ChannelMergerNode\":[\"AudioNode\",\"EventTarget\"],\"ChannelMergerOptions\":[],\"ChannelSplitterNode\":[\"AudioNode\",\"EventTarget\"],\"ChannelSplitterOptions\":[],\"CharacterData\":[\"EventTarget\",\"Node\"],\"CheckerboardReason\":[],\"CheckerboardReport\":[],\"CheckerboardReportService\":[],\"ChromeFilePropertyBag\":[],\"ChromeWorker\":[\"EventTarget\",\"Worker\"],\"Client\":[],\"ClientQueryOptions\":[],\"ClientRectsAndTexts\":[],\"ClientType\":[],\"Clients\":[],\"Clipboard\":[\"EventTarget\"],\"ClipboardEvent\":[\"Event\"],\"ClipboardEventInit\":[],\"ClipboardItem\":[],\"ClipboardItemOptions\":[],\"ClipboardPermissionDescriptor\":[],\"ClipboardUnsanitizedFormats\":[],\"CloseEvent\":[\"Event\"],\"CloseEventInit\":[],\"CodecState\":[],\"CollectedClientData\":[],\"ColorSpaceConversion\":[],\"Comment\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"CompositeOperation\":[],\"CompositionEvent\":[\"Event\",\"UiEvent\"],\"CompositionEventInit\":[],\"CompressionFormat\":[],\"CompressionStream\":[],\"ComputedEffectTiming\":[],\"ConnStatusDict\":[],\"ConnectionType\":[],\"ConsoleCounter\":[],\"ConsoleCounterError\":[],\"ConsoleEvent\":[],\"ConsoleInstance\":[],\"ConsoleInstanceOptions\":[],\"ConsoleLevel\":[],\"ConsoleLogLevel\":[],\"ConsoleProfileEvent\":[],\"ConsoleStackEntry\":[],\"ConsoleTimerError\":[],\"ConsoleTimerLogOrEnd\":[],\"ConsoleTimerStart\":[],\"ConstantSourceNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"ConstantSourceOptions\":[],\"ConstrainBooleanParameters\":[],\"ConstrainDomStringParameters\":[],\"ConstrainDoubleRange\":[],\"ConstrainLongRange\":[],\"ContextAttributes2d\":[],\"ConvertCoordinateOptions\":[],\"ConvolverNode\":[\"AudioNode\",\"EventTarget\"],\"ConvolverOptions\":[],\"CookieChangeEvent\":[\"Event\"],\"CookieChangeEventInit\":[],\"CookieInit\":[],\"CookieListItem\":[],\"CookieSameSite\":[],\"CookieStore\":[\"EventTarget\"],\"CookieStoreDeleteOptions\":[],\"CookieStoreGetOptions\":[],\"CookieStoreManager\":[],\"Coordinates\":[],\"CountQueuingStrategy\":[],\"Credential\":[],\"CredentialCreationOptions\":[],\"CredentialPropertiesOutput\":[],\"CredentialRequestOptions\":[],\"CredentialsContainer\":[],\"Crypto\":[],\"CryptoKey\":[],\"CryptoKeyPair\":[],\"CssAnimation\":[\"Animation\",\"EventTarget\"],\"CssBoxType\":[],\"CssConditionRule\":[\"CssGroupingRule\",\"CssRule\"],\"CssCounterStyleRule\":[\"CssRule\"],\"CssFontFaceRule\":[\"CssRule\"],\"CssFontFeatureValuesRule\":[\"CssRule\"],\"CssGroupingRule\":[\"CssRule\"],\"CssImportRule\":[\"CssRule\"],\"CssKeyframeRule\":[\"CssRule\"],\"CssKeyframesRule\":[\"CssRule\"],\"CssMediaRule\":[\"CssConditionRule\",\"CssGroupingRule\",\"CssRule\"],\"CssNamespaceRule\":[\"CssRule\"],\"CssPageRule\":[\"CssRule\"],\"CssPseudoElement\":[],\"CssRule\":[],\"CssRuleList\":[],\"CssStyleDeclaration\":[],\"CssStyleRule\":[\"CssRule\"],\"CssStyleSheet\":[\"StyleSheet\"],\"CssStyleSheetParsingMode\":[],\"CssSupportsRule\":[\"CssConditionRule\",\"CssGroupingRule\",\"CssRule\"],\"CssTransition\":[\"Animation\",\"EventTarget\"],\"CustomElementRegistry\":[],\"CustomEvent\":[\"Event\"],\"CustomEventInit\":[],\"DataTransfer\":[],\"DataTransferItem\":[],\"DataTransferItemList\":[],\"DateTimeValue\":[],\"DecoderDoctorNotification\":[],\"DecoderDoctorNotificationType\":[],\"DecompressionStream\":[],\"DedicatedWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"DelayNode\":[\"AudioNode\",\"EventTarget\"],\"DelayOptions\":[],\"DeviceAcceleration\":[],\"DeviceAccelerationInit\":[],\"DeviceLightEvent\":[\"Event\"],\"DeviceLightEventInit\":[],\"DeviceMotionEvent\":[\"Event\"],\"DeviceMotionEventInit\":[],\"DeviceOrientationEvent\":[\"Event\"],\"DeviceOrientationEventInit\":[],\"DeviceProximityEvent\":[\"Event\"],\"DeviceProximityEventInit\":[],\"DeviceRotationRate\":[],\"DeviceRotationRateInit\":[],\"DhKeyDeriveParams\":[],\"DirectionSetting\":[],\"Directory\":[],\"DirectoryPickerOptions\":[],\"DisplayMediaStreamConstraints\":[],\"DisplayNameOptions\":[],\"DisplayNameResult\":[],\"DistanceModelType\":[],\"DnsCacheDict\":[],\"DnsCacheEntry\":[],\"DnsLookupDict\":[],\"Document\":[\"EventTarget\",\"Node\"],\"DocumentFragment\":[\"EventTarget\",\"Node\"],\"DocumentTimeline\":[\"AnimationTimeline\"],\"DocumentTimelineOptions\":[],\"DocumentType\":[\"EventTarget\",\"Node\"],\"DomError\":[],\"DomException\":[],\"DomImplementation\":[],\"DomMatrix\":[\"DomMatrixReadOnly\"],\"DomMatrix2dInit\":[],\"DomMatrixInit\":[],\"DomMatrixReadOnly\":[],\"DomParser\":[],\"DomPoint\":[\"DomPointReadOnly\"],\"DomPointInit\":[],\"DomPointReadOnly\":[],\"DomQuad\":[],\"DomQuadInit\":[],\"DomQuadJson\":[],\"DomRect\":[\"DomRectReadOnly\"],\"DomRectInit\":[],\"DomRectList\":[],\"DomRectReadOnly\":[],\"DomRequest\":[\"EventTarget\"],\"DomRequestReadyState\":[],\"DomStringList\":[],\"DomStringMap\":[],\"DomTokenList\":[],\"DomWindowResizeEventDetail\":[],\"DoubleRange\":[],\"DragEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"DragEventInit\":[],\"DynamicsCompressorNode\":[\"AudioNode\",\"EventTarget\"],\"DynamicsCompressorOptions\":[],\"EcKeyAlgorithm\":[],\"EcKeyGenParams\":[],\"EcKeyImportParams\":[],\"EcdhKeyDeriveParams\":[],\"EcdsaParams\":[],\"EffectTiming\":[],\"Element\":[\"EventTarget\",\"Node\"],\"ElementCreationOptions\":[],\"ElementDefinitionOptions\":[],\"EncodedAudioChunk\":[],\"EncodedAudioChunkInit\":[],\"EncodedAudioChunkMetadata\":[],\"EncodedAudioChunkType\":[],\"EncodedVideoChunk\":[],\"EncodedVideoChunkInit\":[],\"EncodedVideoChunkMetadata\":[],\"EncodedVideoChunkType\":[],\"EndingTypes\":[],\"ErrorCallback\":[],\"ErrorEvent\":[\"Event\"],\"ErrorEventInit\":[],\"Event\":[],\"EventInit\":[],\"EventListener\":[],\"EventListenerOptions\":[],\"EventModifierInit\":[],\"EventSource\":[\"EventTarget\"],\"EventSourceInit\":[],\"EventTarget\":[],\"Exception\":[],\"ExtBlendMinmax\":[],\"ExtColorBufferFloat\":[],\"ExtColorBufferHalfFloat\":[],\"ExtDisjointTimerQuery\":[],\"ExtFragDepth\":[],\"ExtSRgb\":[],\"ExtShaderTextureLod\":[],\"ExtTextureFilterAnisotropic\":[],\"ExtTextureNorm16\":[],\"ExtendableCookieChangeEvent\":[\"Event\",\"ExtendableEvent\"],\"ExtendableCookieChangeEventInit\":[],\"ExtendableEvent\":[\"Event\"],\"ExtendableEventInit\":[],\"ExtendableMessageEvent\":[\"Event\",\"ExtendableEvent\"],\"ExtendableMessageEventInit\":[],\"External\":[],\"FakePluginMimeEntry\":[],\"FakePluginTagInit\":[],\"FetchEvent\":[\"Event\",\"ExtendableEvent\"],\"FetchEventInit\":[],\"FetchObserver\":[\"EventTarget\"],\"FetchReadableStreamReadDataArray\":[],\"FetchReadableStreamReadDataDone\":[],\"FetchState\":[],\"File\":[\"Blob\"],\"FileCallback\":[],\"FileList\":[],\"FilePickerAcceptType\":[],\"FilePickerOptions\":[],\"FilePropertyBag\":[],\"FileReader\":[\"EventTarget\"],\"FileReaderSync\":[],\"FileSystem\":[],\"FileSystemCreateWritableOptions\":[],\"FileSystemDirectoryEntry\":[\"FileSystemEntry\"],\"FileSystemDirectoryHandle\":[\"FileSystemHandle\"],\"FileSystemDirectoryReader\":[],\"FileSystemEntriesCallback\":[],\"FileSystemEntry\":[],\"FileSystemEntryCallback\":[],\"FileSystemFileEntry\":[\"FileSystemEntry\"],\"FileSystemFileHandle\":[\"FileSystemHandle\"],\"FileSystemFlags\":[],\"FileSystemGetDirectoryOptions\":[],\"FileSystemGetFileOptions\":[],\"FileSystemHandle\":[],\"FileSystemHandleKind\":[],\"FileSystemHandlePermissionDescriptor\":[],\"FileSystemPermissionDescriptor\":[],\"FileSystemPermissionMode\":[],\"FileSystemReadWriteOptions\":[],\"FileSystemRemoveOptions\":[],\"FileSystemSyncAccessHandle\":[],\"FileSystemWritableFileStream\":[\"WritableStream\"],\"FillMode\":[],\"FlashClassification\":[],\"FlowControlType\":[],\"FocusEvent\":[\"Event\",\"UiEvent\"],\"FocusEventInit\":[],\"FocusOptions\":[],\"FontData\":[],\"FontFace\":[],\"FontFaceDescriptors\":[],\"FontFaceLoadStatus\":[],\"FontFaceSet\":[\"EventTarget\"],\"FontFaceSetIterator\":[],\"FontFaceSetIteratorResult\":[],\"FontFaceSetLoadEvent\":[\"Event\"],\"FontFaceSetLoadEventInit\":[],\"FontFaceSetLoadStatus\":[],\"FormData\":[],\"FrameType\":[],\"FuzzingFunctions\":[],\"GainNode\":[\"AudioNode\",\"EventTarget\"],\"GainOptions\":[],\"Gamepad\":[],\"GamepadButton\":[],\"GamepadEffectParameters\":[],\"GamepadEvent\":[\"Event\"],\"GamepadEventInit\":[],\"GamepadHand\":[],\"GamepadHapticActuator\":[],\"GamepadHapticActuatorType\":[],\"GamepadHapticEffectType\":[],\"GamepadHapticsResult\":[],\"GamepadMappingType\":[],\"GamepadPose\":[],\"GamepadTouch\":[],\"Geolocation\":[],\"GestureEvent\":[\"Event\",\"UiEvent\"],\"GetAnimationsOptions\":[],\"GetRootNodeOptions\":[],\"GetUserMediaRequest\":[],\"Gpu\":[],\"GpuAdapter\":[],\"GpuAdapterInfo\":[],\"GpuAddressMode\":[],\"GpuAutoLayoutMode\":[],\"GpuBindGroup\":[],\"GpuBindGroupDescriptor\":[],\"GpuBindGroupEntry\":[],\"GpuBindGroupLayout\":[],\"GpuBindGroupLayoutDescriptor\":[],\"GpuBindGroupLayoutEntry\":[],\"GpuBlendComponent\":[],\"GpuBlendFactor\":[],\"GpuBlendOperation\":[],\"GpuBlendState\":[],\"GpuBuffer\":[],\"GpuBufferBinding\":[],\"GpuBufferBindingLayout\":[],\"GpuBufferBindingType\":[],\"GpuBufferDescriptor\":[],\"GpuBufferMapState\":[],\"GpuCanvasAlphaMode\":[],\"GpuCanvasConfiguration\":[],\"GpuCanvasContext\":[],\"GpuCanvasToneMapping\":[],\"GpuCanvasToneMappingMode\":[],\"GpuColorDict\":[],\"GpuColorTargetState\":[],\"GpuCommandBuffer\":[],\"GpuCommandBufferDescriptor\":[],\"GpuCommandEncoder\":[],\"GpuCommandEncoderDescriptor\":[],\"GpuCompareFunction\":[],\"GpuCompilationInfo\":[],\"GpuCompilationMessage\":[],\"GpuCompilationMessageType\":[],\"GpuComputePassDescriptor\":[],\"GpuComputePassEncoder\":[],\"GpuComputePassTimestampWrites\":[],\"GpuComputePipeline\":[],\"GpuComputePipelineDescriptor\":[],\"GpuCopyExternalImageDestInfo\":[],\"GpuCopyExternalImageSourceInfo\":[],\"GpuCullMode\":[],\"GpuDepthStencilState\":[],\"GpuDevice\":[\"EventTarget\"],\"GpuDeviceDescriptor\":[],\"GpuDeviceLostInfo\":[],\"GpuDeviceLostReason\":[],\"GpuError\":[],\"GpuErrorFilter\":[],\"GpuExtent3dDict\":[],\"GpuExternalTexture\":[],\"GpuExternalTextureBindingLayout\":[],\"GpuExternalTextureDescriptor\":[],\"GpuFeatureName\":[],\"GpuFilterMode\":[],\"GpuFragmentState\":[],\"GpuFrontFace\":[],\"GpuIndexFormat\":[],\"GpuInternalError\":[\"GpuError\"],\"GpuLoadOp\":[],\"GpuMipmapFilterMode\":[],\"GpuMultisampleState\":[],\"GpuObjectDescriptorBase\":[],\"GpuOrigin2dDict\":[],\"GpuOrigin3dDict\":[],\"GpuOutOfMemoryError\":[\"GpuError\"],\"GpuPipelineDescriptorBase\":[],\"GpuPipelineError\":[\"DomException\"],\"GpuPipelineErrorInit\":[],\"GpuPipelineErrorReason\":[],\"GpuPipelineLayout\":[],\"GpuPipelineLayoutDescriptor\":[],\"GpuPowerPreference\":[],\"GpuPrimitiveState\":[],\"GpuPrimitiveTopology\":[],\"GpuProgrammableStage\":[],\"GpuQuerySet\":[],\"GpuQuerySetDescriptor\":[],\"GpuQueryType\":[],\"GpuQueue\":[],\"GpuQueueDescriptor\":[],\"GpuRenderBundle\":[],\"GpuRenderBundleDescriptor\":[],\"GpuRenderBundleEncoder\":[],\"GpuRenderBundleEncoderDescriptor\":[],\"GpuRenderPassColorAttachment\":[],\"GpuRenderPassDepthStencilAttachment\":[],\"GpuRenderPassDescriptor\":[],\"GpuRenderPassEncoder\":[],\"GpuRenderPassLayout\":[],\"GpuRenderPassTimestampWrites\":[],\"GpuRenderPipeline\":[],\"GpuRenderPipelineDescriptor\":[],\"GpuRequestAdapterOptions\":[],\"GpuSampler\":[],\"GpuSamplerBindingLayout\":[],\"GpuSamplerBindingType\":[],\"GpuSamplerDescriptor\":[],\"GpuShaderModule\":[],\"GpuShaderModuleCompilationHint\":[],\"GpuShaderModuleDescriptor\":[],\"GpuStencilFaceState\":[],\"GpuStencilOperation\":[],\"GpuStorageTextureAccess\":[],\"GpuStorageTextureBindingLayout\":[],\"GpuStoreOp\":[],\"GpuSupportedFeatures\":[],\"GpuSupportedLimits\":[],\"GpuTexelCopyBufferInfo\":[],\"GpuTexelCopyBufferLayout\":[],\"GpuTexelCopyTextureInfo\":[],\"GpuTexture\":[],\"GpuTextureAspect\":[],\"GpuTextureBindingLayout\":[],\"GpuTextureDescriptor\":[],\"GpuTextureDimension\":[],\"GpuTextureFormat\":[],\"GpuTextureSampleType\":[],\"GpuTextureView\":[],\"GpuTextureViewDescriptor\":[],\"GpuTextureViewDimension\":[],\"GpuUncapturedErrorEvent\":[\"Event\"],\"GpuUncapturedErrorEventInit\":[],\"GpuValidationError\":[\"GpuError\"],\"GpuVertexAttribute\":[],\"GpuVertexBufferLayout\":[],\"GpuVertexFormat\":[],\"GpuVertexState\":[],\"GpuVertexStepMode\":[],\"GroupedHistoryEventInit\":[],\"HalfOpenInfoDict\":[],\"HardwareAcceleration\":[],\"HashChangeEvent\":[\"Event\"],\"HashChangeEventInit\":[],\"Headers\":[],\"HeadersGuardEnum\":[],\"Hid\":[\"EventTarget\"],\"HidCollectionInfo\":[],\"HidConnectionEvent\":[\"Event\"],\"HidConnectionEventInit\":[],\"HidDevice\":[\"EventTarget\"],\"HidDeviceFilter\":[],\"HidDeviceRequestOptions\":[],\"HidInputReportEvent\":[\"Event\"],\"HidInputReportEventInit\":[],\"HidReportInfo\":[],\"HidReportItem\":[],\"HidUnitSystem\":[],\"HiddenPluginEventInit\":[],\"History\":[],\"HitRegionOptions\":[],\"HkdfParams\":[],\"HmacDerivedKeyParams\":[],\"HmacImportParams\":[],\"HmacKeyAlgorithm\":[],\"HmacKeyGenParams\":[],\"HtmlAllCollection\":[],\"HtmlAnchorElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlAreaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlAudioElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"HtmlMediaElement\",\"Node\"],\"HtmlBaseElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlBodyElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlBrElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlButtonElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlCanvasElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlCollection\":[],\"HtmlDListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDataElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDataListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDetailsElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDialogElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDirectoryElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDivElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlDocument\":[\"Document\",\"EventTarget\",\"Node\"],\"HtmlElement\":[\"Element\",\"EventTarget\",\"Node\"],\"HtmlEmbedElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFieldSetElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFontElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFormControlsCollection\":[\"HtmlCollection\"],\"HtmlFormElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFrameElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlFrameSetElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHeadElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHeadingElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHrElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlHtmlElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlIFrameElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlImageElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlInputElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLabelElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLegendElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLiElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlLinkElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMapElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMediaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMenuElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMenuItemElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMetaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlMeterElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlModElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlObjectElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptGroupElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlOptionsCollection\":[\"HtmlCollection\"],\"HtmlOutputElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlParagraphElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlParamElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlPictureElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlPreElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlProgressElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlQuoteElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlScriptElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSelectElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSlotElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSourceElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlSpanElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlStyleElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableCaptionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableCellElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableColElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableRowElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTableSectionElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTemplateElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTextAreaElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTimeElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTitleElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlTrackElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlUListElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlUnknownElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"Node\"],\"HtmlVideoElement\":[\"Element\",\"EventTarget\",\"HtmlElement\",\"HtmlMediaElement\",\"Node\"],\"HttpConnDict\":[],\"HttpConnInfo\":[],\"HttpConnectionElement\":[],\"IdbCursor\":[],\"IdbCursorDirection\":[],\"IdbCursorWithValue\":[\"IdbCursor\"],\"IdbDatabase\":[\"EventTarget\"],\"IdbFactory\":[],\"IdbFileHandle\":[\"EventTarget\"],\"IdbFileMetadataParameters\":[],\"IdbFileRequest\":[\"DomRequest\",\"EventTarget\"],\"IdbIndex\":[],\"IdbIndexParameters\":[],\"IdbKeyRange\":[],\"IdbLocaleAwareKeyRange\":[\"IdbKeyRange\"],\"IdbMutableFile\":[\"EventTarget\"],\"IdbObjectStore\":[],\"IdbObjectStoreParameters\":[],\"IdbOpenDbOptions\":[],\"IdbOpenDbRequest\":[\"EventTarget\",\"IdbRequest\"],\"IdbRequest\":[\"EventTarget\"],\"IdbRequestReadyState\":[],\"IdbTransaction\":[\"EventTarget\"],\"IdbTransactionDurability\":[],\"IdbTransactionMode\":[],\"IdbTransactionOptions\":[],\"IdbVersionChangeEvent\":[\"Event\"],\"IdbVersionChangeEventInit\":[],\"IdleDeadline\":[],\"IdleRequestOptions\":[],\"IirFilterNode\":[\"AudioNode\",\"EventTarget\"],\"IirFilterOptions\":[],\"ImageBitmap\":[],\"ImageBitmapOptions\":[],\"ImageBitmapRenderingContext\":[],\"ImageCapture\":[],\"ImageCaptureError\":[],\"ImageCaptureErrorEvent\":[\"Event\"],\"ImageCaptureErrorEventInit\":[],\"ImageData\":[],\"ImageDecodeOptions\":[],\"ImageDecodeResult\":[],\"ImageDecoder\":[],\"ImageDecoderInit\":[],\"ImageEncodeOptions\":[],\"ImageOrientation\":[],\"ImageTrack\":[\"EventTarget\"],\"ImageTrackList\":[],\"InputDeviceInfo\":[\"MediaDeviceInfo\"],\"InputEvent\":[\"Event\",\"UiEvent\"],\"InputEventInit\":[],\"IntersectionObserver\":[],\"IntersectionObserverEntry\":[],\"IntersectionObserverEntryInit\":[],\"IntersectionObserverInit\":[],\"IntlUtils\":[],\"IsInputPendingOptions\":[],\"IterableKeyAndValueResult\":[],\"IterableKeyOrValueResult\":[],\"IterationCompositeOperation\":[],\"JsonWebKey\":[],\"KeyAlgorithm\":[],\"KeyEvent\":[],\"KeyFrameRequestEvent\":[\"Event\"],\"KeyIdsInitData\":[],\"KeyboardEvent\":[\"Event\",\"UiEvent\"],\"KeyboardEventInit\":[],\"KeyframeAnimationOptions\":[],\"KeyframeEffect\":[\"AnimationEffect\"],\"KeyframeEffectOptions\":[],\"L10nElement\":[],\"L10nValue\":[],\"LargeBlobSupport\":[],\"LatencyMode\":[],\"LifecycleCallbacks\":[],\"LineAlignSetting\":[],\"ListBoxObject\":[],\"LocalMediaStream\":[\"EventTarget\",\"MediaStream\"],\"LocaleInfo\":[],\"Location\":[],\"Lock\":[],\"LockInfo\":[],\"LockManager\":[],\"LockManagerSnapshot\":[],\"LockMode\":[],\"LockOptions\":[],\"MathMlElement\":[\"Element\",\"EventTarget\",\"Node\"],\"MediaCapabilities\":[],\"MediaCapabilitiesInfo\":[],\"MediaConfiguration\":[],\"MediaDecodingConfiguration\":[],\"MediaDecodingType\":[],\"MediaDeviceInfo\":[],\"MediaDeviceKind\":[],\"MediaDevices\":[\"EventTarget\"],\"MediaElementAudioSourceNode\":[\"AudioNode\",\"EventTarget\"],\"MediaElementAudioSourceOptions\":[],\"MediaEncodingConfiguration\":[],\"MediaEncodingType\":[],\"MediaEncryptedEvent\":[\"Event\"],\"MediaError\":[],\"MediaImage\":[],\"MediaKeyError\":[\"Event\"],\"MediaKeyMessageEvent\":[\"Event\"],\"MediaKeyMessageEventInit\":[],\"MediaKeyMessageType\":[],\"MediaKeyNeededEventInit\":[],\"MediaKeySession\":[\"EventTarget\"],\"MediaKeySessionType\":[],\"MediaKeyStatus\":[],\"MediaKeyStatusMap\":[],\"MediaKeySystemAccess\":[],\"MediaKeySystemConfiguration\":[],\"MediaKeySystemMediaCapability\":[],\"MediaKeySystemStatus\":[],\"MediaKeys\":[],\"MediaKeysPolicy\":[],\"MediaKeysRequirement\":[],\"MediaList\":[],\"MediaMetadata\":[],\"MediaMetadataInit\":[],\"MediaPositionState\":[],\"MediaQueryList\":[\"EventTarget\"],\"MediaQueryListEvent\":[\"Event\"],\"MediaQueryListEventInit\":[],\"MediaRecorder\":[\"EventTarget\"],\"MediaRecorderErrorEvent\":[\"Event\"],\"MediaRecorderErrorEventInit\":[],\"MediaRecorderOptions\":[],\"MediaSession\":[],\"MediaSessionAction\":[],\"MediaSessionActionDetails\":[],\"MediaSessionPlaybackState\":[],\"MediaSource\":[\"EventTarget\"],\"MediaSourceEndOfStreamError\":[],\"MediaSourceEnum\":[],\"MediaSourceReadyState\":[],\"MediaStream\":[\"EventTarget\"],\"MediaStreamAudioDestinationNode\":[\"AudioNode\",\"EventTarget\"],\"MediaStreamAudioSourceNode\":[\"AudioNode\",\"EventTarget\"],\"MediaStreamAudioSourceOptions\":[],\"MediaStreamConstraints\":[],\"MediaStreamError\":[],\"MediaStreamEvent\":[\"Event\"],\"MediaStreamEventInit\":[],\"MediaStreamTrack\":[\"EventTarget\"],\"MediaStreamTrackEvent\":[\"Event\"],\"MediaStreamTrackEventInit\":[],\"MediaStreamTrackGenerator\":[\"EventTarget\",\"MediaStreamTrack\"],\"MediaStreamTrackGeneratorInit\":[],\"MediaStreamTrackProcessor\":[],\"MediaStreamTrackProcessorInit\":[],\"MediaStreamTrackState\":[],\"MediaTrackCapabilities\":[],\"MediaTrackConstraintSet\":[],\"MediaTrackConstraints\":[],\"MediaTrackSettings\":[],\"MediaTrackSupportedConstraints\":[],\"MemoryAttribution\":[],\"MemoryAttributionContainer\":[],\"MemoryBreakdownEntry\":[],\"MemoryMeasurement\":[],\"MessageChannel\":[],\"MessageEvent\":[\"Event\"],\"MessageEventInit\":[],\"MessagePort\":[\"EventTarget\"],\"MidiAccess\":[\"EventTarget\"],\"MidiConnectionEvent\":[\"Event\"],\"MidiConnectionEventInit\":[],\"MidiInput\":[\"EventTarget\",\"MidiPort\"],\"MidiInputMap\":[],\"MidiMessageEvent\":[\"Event\"],\"MidiMessageEventInit\":[],\"MidiOptions\":[],\"MidiOutput\":[\"EventTarget\",\"MidiPort\"],\"MidiOutputMap\":[],\"MidiPort\":[\"EventTarget\"],\"MidiPortConnectionState\":[],\"MidiPortDeviceState\":[],\"MidiPortType\":[],\"MimeType\":[],\"MimeTypeArray\":[],\"MouseEvent\":[\"Event\",\"UiEvent\"],\"MouseEventInit\":[],\"MouseScrollEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"MozDebug\":[],\"MutationEvent\":[\"Event\"],\"MutationObserver\":[],\"MutationObserverInit\":[],\"MutationObservingInfo\":[],\"MutationRecord\":[],\"NamedNodeMap\":[],\"NativeOsFileReadOptions\":[],\"NativeOsFileWriteAtomicOptions\":[],\"NavigationType\":[],\"Navigator\":[],\"NavigatorAutomationInformation\":[],\"NavigatorUaBrandVersion\":[],\"NavigatorUaData\":[],\"NetworkCommandOptions\":[],\"NetworkInformation\":[\"EventTarget\"],\"NetworkResultOptions\":[],\"Node\":[\"EventTarget\"],\"NodeFilter\":[],\"NodeIterator\":[],\"NodeList\":[],\"Notification\":[\"EventTarget\"],\"NotificationAction\":[],\"NotificationDirection\":[],\"NotificationEvent\":[\"Event\",\"ExtendableEvent\"],\"NotificationEventInit\":[],\"NotificationOptions\":[],\"NotificationPermission\":[],\"ObserverCallback\":[],\"OesElementIndexUint\":[],\"OesStandardDerivatives\":[],\"OesTextureFloat\":[],\"OesTextureFloatLinear\":[],\"OesTextureHalfFloat\":[],\"OesTextureHalfFloatLinear\":[],\"OesVertexArrayObject\":[],\"OfflineAudioCompletionEvent\":[\"Event\"],\"OfflineAudioCompletionEventInit\":[],\"OfflineAudioContext\":[\"BaseAudioContext\",\"EventTarget\"],\"OfflineAudioContextOptions\":[],\"OfflineResourceList\":[\"EventTarget\"],\"OffscreenCanvas\":[\"EventTarget\"],\"OffscreenCanvasRenderingContext2d\":[],\"OpenFilePickerOptions\":[],\"OpenWindowEventDetail\":[],\"OptionalEffectTiming\":[],\"OrientationLockType\":[],\"OrientationType\":[],\"OscillatorNode\":[\"AudioNode\",\"AudioScheduledSourceNode\",\"EventTarget\"],\"OscillatorOptions\":[],\"OscillatorType\":[],\"OverSampleType\":[],\"OvrMultiview2\":[],\"PageTransitionEvent\":[\"Event\"],\"PageTransitionEventInit\":[],\"PaintRequest\":[],\"PaintRequestList\":[],\"PaintWorkletGlobalScope\":[\"WorkletGlobalScope\"],\"PannerNode\":[\"AudioNode\",\"EventTarget\"],\"PannerOptions\":[],\"PanningModelType\":[],\"ParityType\":[],\"Path2d\":[],\"PaymentAddress\":[],\"PaymentComplete\":[],\"PaymentMethodChangeEvent\":[\"Event\",\"PaymentRequestUpdateEvent\"],\"PaymentMethodChangeEventInit\":[],\"PaymentRequestUpdateEvent\":[\"Event\"],\"PaymentRequestUpdateEventInit\":[],\"PaymentResponse\":[],\"Pbkdf2Params\":[],\"PcImplIceConnectionState\":[],\"PcImplIceGatheringState\":[],\"PcImplSignalingState\":[],\"PcObserverStateType\":[],\"Performance\":[\"EventTarget\"],\"PerformanceEntry\":[],\"PerformanceEntryEventInit\":[],\"PerformanceEntryFilterOptions\":[],\"PerformanceMark\":[\"PerformanceEntry\"],\"PerformanceMeasure\":[\"PerformanceEntry\"],\"PerformanceNavigation\":[],\"PerformanceNavigationTiming\":[\"PerformanceEntry\",\"PerformanceResourceTiming\"],\"PerformanceObserver\":[],\"PerformanceObserverEntryList\":[],\"PerformanceObserverInit\":[],\"PerformanceResourceTiming\":[\"PerformanceEntry\"],\"PerformanceServerTiming\":[],\"PerformanceTiming\":[],\"PeriodicWave\":[],\"PeriodicWaveConstraints\":[],\"PeriodicWaveOptions\":[],\"PermissionDescriptor\":[],\"PermissionName\":[],\"PermissionState\":[],\"PermissionStatus\":[\"EventTarget\"],\"Permissions\":[],\"PictureInPictureEvent\":[\"Event\"],\"PictureInPictureEventInit\":[],\"PictureInPictureWindow\":[\"EventTarget\"],\"PlaneLayout\":[],\"PlaybackDirection\":[],\"Plugin\":[],\"PluginArray\":[],\"PluginCrashedEventInit\":[],\"PointerEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"PointerEventInit\":[],\"PopStateEvent\":[\"Event\"],\"PopStateEventInit\":[],\"PopupBlockedEvent\":[\"Event\"],\"PopupBlockedEventInit\":[],\"Position\":[],\"PositionAlignSetting\":[],\"PositionError\":[],\"PositionOptions\":[],\"PremultiplyAlpha\":[],\"Presentation\":[],\"PresentationAvailability\":[\"EventTarget\"],\"PresentationConnection\":[\"EventTarget\"],\"PresentationConnectionAvailableEvent\":[\"Event\"],\"PresentationConnectionAvailableEventInit\":[],\"PresentationConnectionBinaryType\":[],\"PresentationConnectionCloseEvent\":[\"Event\"],\"PresentationConnectionCloseEventInit\":[],\"PresentationConnectionClosedReason\":[],\"PresentationConnectionList\":[\"EventTarget\"],\"PresentationConnectionState\":[],\"PresentationReceiver\":[],\"PresentationRequest\":[\"EventTarget\"],\"PresentationStyle\":[],\"ProcessingInstruction\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"ProfileTimelineLayerRect\":[],\"ProfileTimelineMarker\":[],\"ProfileTimelineMessagePortOperationType\":[],\"ProfileTimelineStackFrame\":[],\"ProfileTimelineWorkerOperationType\":[],\"ProgressEvent\":[\"Event\"],\"ProgressEventInit\":[],\"PromiseNativeHandler\":[],\"PromiseRejectionEvent\":[\"Event\"],\"PromiseRejectionEventInit\":[],\"PublicKeyCredential\":[\"Credential\"],\"PublicKeyCredentialCreationOptions\":[],\"PublicKeyCredentialCreationOptionsJson\":[],\"PublicKeyCredentialDescriptor\":[],\"PublicKeyCredentialDescriptorJson\":[],\"PublicKeyCredentialEntity\":[],\"PublicKeyCredentialHints\":[],\"PublicKeyCredentialParameters\":[],\"PublicKeyCredentialRequestOptions\":[],\"PublicKeyCredentialRequestOptionsJson\":[],\"PublicKeyCredentialRpEntity\":[],\"PublicKeyCredentialType\":[],\"PublicKeyCredentialUserEntity\":[],\"PublicKeyCredentialUserEntityJson\":[],\"PushEncryptionKeyName\":[],\"PushEvent\":[\"Event\",\"ExtendableEvent\"],\"PushEventInit\":[],\"PushManager\":[],\"PushMessageData\":[],\"PushPermissionState\":[],\"PushSubscription\":[],\"PushSubscriptionInit\":[],\"PushSubscriptionJson\":[],\"PushSubscriptionKeys\":[],\"PushSubscriptionOptions\":[],\"PushSubscriptionOptionsInit\":[],\"QueryOptions\":[],\"QueuingStrategy\":[],\"QueuingStrategyInit\":[],\"RadioNodeList\":[\"NodeList\"],\"Range\":[],\"RcwnPerfStats\":[],\"RcwnStatus\":[],\"ReadableByteStreamController\":[],\"ReadableStream\":[],\"ReadableStreamByobReader\":[],\"ReadableStreamByobRequest\":[],\"ReadableStreamDefaultController\":[],\"ReadableStreamDefaultReader\":[],\"ReadableStreamGetReaderOptions\":[],\"ReadableStreamIteratorOptions\":[],\"ReadableStreamReadResult\":[],\"ReadableStreamReaderMode\":[],\"ReadableStreamType\":[],\"ReadableWritablePair\":[],\"RecordingState\":[],\"ReferrerPolicy\":[],\"RegisterRequest\":[],\"RegisterResponse\":[],\"RegisteredKey\":[],\"RegistrationOptions\":[],\"RegistrationResponseJson\":[],\"Request\":[],\"RequestCache\":[],\"RequestCredentials\":[],\"RequestDestination\":[],\"RequestDeviceOptions\":[],\"RequestInit\":[],\"RequestMediaKeySystemAccessNotification\":[],\"RequestMode\":[],\"RequestRedirect\":[],\"ResidentKeyRequirement\":[],\"ResizeObserver\":[],\"ResizeObserverBoxOptions\":[],\"ResizeObserverEntry\":[],\"ResizeObserverOptions\":[],\"ResizeObserverSize\":[],\"ResizeQuality\":[],\"Response\":[],\"ResponseInit\":[],\"ResponseType\":[],\"RsaHashedImportParams\":[],\"RsaOaepParams\":[],\"RsaOtherPrimesInfo\":[],\"RsaPssParams\":[],\"RtcAnswerOptions\":[],\"RtcBundlePolicy\":[],\"RtcCertificate\":[],\"RtcCertificateExpiration\":[],\"RtcCodecStats\":[],\"RtcConfiguration\":[],\"RtcDataChannel\":[\"EventTarget\"],\"RtcDataChannelEvent\":[\"Event\"],\"RtcDataChannelEventInit\":[],\"RtcDataChannelInit\":[],\"RtcDataChannelState\":[],\"RtcDataChannelType\":[],\"RtcDegradationPreference\":[],\"RtcEncodedAudioFrame\":[],\"RtcEncodedAudioFrameMetadata\":[],\"RtcEncodedAudioFrameOptions\":[],\"RtcEncodedVideoFrame\":[],\"RtcEncodedVideoFrameMetadata\":[],\"RtcEncodedVideoFrameOptions\":[],\"RtcEncodedVideoFrameType\":[],\"RtcFecParameters\":[],\"RtcIceCandidate\":[],\"RtcIceCandidateInit\":[],\"RtcIceCandidatePairStats\":[],\"RtcIceCandidateStats\":[],\"RtcIceComponentStats\":[],\"RtcIceConnectionState\":[],\"RtcIceCredentialType\":[],\"RtcIceGatheringState\":[],\"RtcIceServer\":[],\"RtcIceTransportPolicy\":[],\"RtcIdentityAssertion\":[],\"RtcIdentityAssertionResult\":[],\"RtcIdentityProvider\":[],\"RtcIdentityProviderDetails\":[],\"RtcIdentityProviderOptions\":[],\"RtcIdentityProviderRegistrar\":[],\"RtcIdentityValidationResult\":[],\"RtcInboundRtpStreamStats\":[],\"RtcMediaStreamStats\":[],\"RtcMediaStreamTrackStats\":[],\"RtcOfferAnswerOptions\":[],\"RtcOfferOptions\":[],\"RtcOutboundRtpStreamStats\":[],\"RtcPeerConnection\":[\"EventTarget\"],\"RtcPeerConnectionIceErrorEvent\":[\"Event\"],\"RtcPeerConnectionIceEvent\":[\"Event\"],\"RtcPeerConnectionIceEventInit\":[],\"RtcPeerConnectionState\":[],\"RtcPriorityType\":[],\"RtcRtcpParameters\":[],\"RtcRtpCapabilities\":[],\"RtcRtpCodecCapability\":[],\"RtcRtpCodecParameters\":[],\"RtcRtpContributingSource\":[],\"RtcRtpEncodingParameters\":[],\"RtcRtpHeaderExtensionCapability\":[],\"RtcRtpHeaderExtensionParameters\":[],\"RtcRtpParameters\":[],\"RtcRtpReceiver\":[],\"RtcRtpScriptTransform\":[],\"RtcRtpScriptTransformer\":[\"EventTarget\"],\"RtcRtpSender\":[],\"RtcRtpSourceEntry\":[],\"RtcRtpSourceEntryType\":[],\"RtcRtpSynchronizationSource\":[],\"RtcRtpTransceiver\":[],\"RtcRtpTransceiverDirection\":[],\"RtcRtpTransceiverInit\":[],\"RtcRtxParameters\":[],\"RtcSdpType\":[],\"RtcSessionDescription\":[],\"RtcSessionDescriptionInit\":[],\"RtcSignalingState\":[],\"RtcStats\":[],\"RtcStatsIceCandidatePairState\":[],\"RtcStatsIceCandidateType\":[],\"RtcStatsReport\":[],\"RtcStatsReportInternal\":[],\"RtcStatsType\":[],\"RtcTrackEvent\":[\"Event\"],\"RtcTrackEventInit\":[],\"RtcTransformEvent\":[\"Event\"],\"RtcTransportStats\":[],\"RtcdtmfSender\":[\"EventTarget\"],\"RtcdtmfToneChangeEvent\":[\"Event\"],\"RtcdtmfToneChangeEventInit\":[],\"RtcrtpContributingSourceStats\":[],\"RtcrtpStreamStats\":[],\"SFrameTransform\":[\"EventTarget\"],\"SFrameTransformErrorEvent\":[\"Event\"],\"SFrameTransformErrorEventInit\":[],\"SFrameTransformErrorEventType\":[],\"SFrameTransformOptions\":[],\"SFrameTransformRole\":[],\"SaveFilePickerOptions\":[],\"Scheduler\":[],\"SchedulerPostTaskOptions\":[],\"Scheduling\":[],\"Screen\":[\"EventTarget\"],\"ScreenColorGamut\":[],\"ScreenDetailed\":[\"EventTarget\",\"Screen\"],\"ScreenDetails\":[\"EventTarget\"],\"ScreenLuminance\":[],\"ScreenOrientation\":[\"EventTarget\"],\"ScriptProcessorNode\":[\"AudioNode\",\"EventTarget\"],\"ScrollAreaEvent\":[\"Event\",\"UiEvent\"],\"ScrollBehavior\":[],\"ScrollBoxObject\":[],\"ScrollIntoViewContainer\":[],\"ScrollIntoViewOptions\":[],\"ScrollLogicalPosition\":[],\"ScrollOptions\":[],\"ScrollRestoration\":[],\"ScrollSetting\":[],\"ScrollState\":[],\"ScrollToOptions\":[],\"ScrollViewChangeEventInit\":[],\"SecurityPolicyViolationEvent\":[\"Event\"],\"SecurityPolicyViolationEventDisposition\":[],\"SecurityPolicyViolationEventInit\":[],\"Selection\":[],\"SelectionMode\":[],\"Serial\":[\"EventTarget\"],\"SerialInputSignals\":[],\"SerialOptions\":[],\"SerialOutputSignals\":[],\"SerialPort\":[\"EventTarget\"],\"SerialPortFilter\":[],\"SerialPortInfo\":[],\"SerialPortRequestOptions\":[],\"ServerSocketOptions\":[],\"ServiceWorker\":[\"EventTarget\"],\"ServiceWorkerContainer\":[\"EventTarget\"],\"ServiceWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"ServiceWorkerRegistration\":[\"EventTarget\"],\"ServiceWorkerState\":[],\"ServiceWorkerUpdateViaCache\":[],\"ShadowRoot\":[\"DocumentFragment\",\"EventTarget\",\"Node\"],\"ShadowRootInit\":[],\"ShadowRootMode\":[],\"ShareData\":[],\"SharedWorker\":[\"EventTarget\"],\"SharedWorkerGlobalScope\":[\"EventTarget\",\"WorkerGlobalScope\"],\"SignResponse\":[],\"SocketElement\":[],\"SocketOptions\":[],\"SocketReadyState\":[],\"SocketsDict\":[],\"SourceBuffer\":[\"EventTarget\"],\"SourceBufferAppendMode\":[],\"SourceBufferList\":[\"EventTarget\"],\"SpeechGrammar\":[],\"SpeechGrammarList\":[],\"SpeechRecognition\":[\"EventTarget\"],\"SpeechRecognitionAlternative\":[],\"SpeechRecognitionError\":[\"Event\"],\"SpeechRecognitionErrorCode\":[],\"SpeechRecognitionErrorInit\":[],\"SpeechRecognitionEvent\":[\"Event\"],\"SpeechRecognitionEventInit\":[],\"SpeechRecognitionResult\":[],\"SpeechRecognitionResultList\":[],\"SpeechSynthesis\":[\"EventTarget\"],\"SpeechSynthesisErrorCode\":[],\"SpeechSynthesisErrorEvent\":[\"Event\",\"SpeechSynthesisEvent\"],\"SpeechSynthesisErrorEventInit\":[],\"SpeechSynthesisEvent\":[\"Event\"],\"SpeechSynthesisEventInit\":[],\"SpeechSynthesisUtterance\":[\"EventTarget\"],\"SpeechSynthesisVoice\":[],\"StereoPannerNode\":[\"AudioNode\",\"EventTarget\"],\"StereoPannerOptions\":[],\"Storage\":[],\"StorageEstimate\":[],\"StorageEvent\":[\"Event\"],\"StorageEventInit\":[],\"StorageManager\":[],\"StorageType\":[],\"StreamPipeOptions\":[],\"StyleRuleChangeEventInit\":[],\"StyleSheet\":[],\"StyleSheetApplicableStateChangeEventInit\":[],\"StyleSheetChangeEventInit\":[],\"StyleSheetList\":[],\"SubmitEvent\":[\"Event\"],\"SubmitEventInit\":[],\"SubtleCrypto\":[],\"SupportedType\":[],\"SvcOutputMetadata\":[],\"SvgAngle\":[],\"SvgAnimateElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimateMotionElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimateTransformElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgAnimatedAngle\":[],\"SvgAnimatedBoolean\":[],\"SvgAnimatedEnumeration\":[],\"SvgAnimatedInteger\":[],\"SvgAnimatedLength\":[],\"SvgAnimatedLengthList\":[],\"SvgAnimatedNumber\":[],\"SvgAnimatedNumberList\":[],\"SvgAnimatedPreserveAspectRatio\":[],\"SvgAnimatedRect\":[],\"SvgAnimatedString\":[],\"SvgAnimatedTransformList\":[],\"SvgAnimationElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgBoundingBoxOptions\":[],\"SvgCircleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgClipPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgComponentTransferFunctionElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgDefsElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgDescElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgElement\":[\"Element\",\"EventTarget\",\"Node\"],\"SvgEllipseElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgFilterElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgForeignObjectElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgGeometryElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgGraphicsElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgImageElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgLength\":[],\"SvgLengthList\":[],\"SvgLineElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgLinearGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGradientElement\"],\"SvgMarkerElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgMaskElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgMatrix\":[],\"SvgMetadataElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgNumber\":[],\"SvgNumberList\":[],\"SvgPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPathSeg\":[],\"SvgPathSegArcAbs\":[\"SvgPathSeg\"],\"SvgPathSegArcRel\":[\"SvgPathSeg\"],\"SvgPathSegClosePath\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicSmoothAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoCubicSmoothRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticRel\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticSmoothAbs\":[\"SvgPathSeg\"],\"SvgPathSegCurvetoQuadraticSmoothRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoHorizontalAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoHorizontalRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoRel\":[\"SvgPathSeg\"],\"SvgPathSegLinetoVerticalAbs\":[\"SvgPathSeg\"],\"SvgPathSegLinetoVerticalRel\":[\"SvgPathSeg\"],\"SvgPathSegList\":[],\"SvgPathSegMovetoAbs\":[\"SvgPathSeg\"],\"SvgPathSegMovetoRel\":[\"SvgPathSeg\"],\"SvgPatternElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgPoint\":[],\"SvgPointList\":[],\"SvgPolygonElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPolylineElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgPreserveAspectRatio\":[],\"SvgRadialGradientElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGradientElement\"],\"SvgRect\":[],\"SvgRectElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGeometryElement\",\"SvgGraphicsElement\"],\"SvgScriptElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgSetElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgAnimationElement\",\"SvgElement\"],\"SvgStopElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgStringList\":[],\"SvgStyleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgSwitchElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgSymbolElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgTextContentElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgTextElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\",\"SvgTextPositioningElement\"],\"SvgTextPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\"],\"SvgTextPositioningElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\"],\"SvgTitleElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgTransform\":[],\"SvgTransformList\":[],\"SvgUnitTypes\":[],\"SvgUseElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgViewElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgZoomAndPan\":[],\"SvgaElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgfeBlendElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeColorMatrixElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeComponentTransferElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeCompositeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeConvolveMatrixElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDiffuseLightingElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDisplacementMapElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDistantLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeDropShadowElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeFloodElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeFuncAElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncBElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncGElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeFuncRElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgComponentTransferFunctionElement\",\"SvgElement\"],\"SvgfeGaussianBlurElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeImageElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMergeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMergeNodeElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeMorphologyElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeOffsetElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfePointLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeSpecularLightingElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeSpotLightElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeTileElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgfeTurbulenceElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvggElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgmPathElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\"],\"SvgsvgElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\"],\"SvgtSpanElement\":[\"Element\",\"EventTarget\",\"Node\",\"SvgElement\",\"SvgGraphicsElement\",\"SvgTextContentElement\",\"SvgTextPositioningElement\"],\"TaskController\":[\"AbortController\"],\"TaskControllerInit\":[],\"TaskPriority\":[],\"TaskPriorityChangeEvent\":[\"Event\"],\"TaskPriorityChangeEventInit\":[],\"TaskSignal\":[\"AbortSignal\",\"EventTarget\"],\"TaskSignalAnyInit\":[],\"TcpReadyState\":[],\"TcpServerSocket\":[\"EventTarget\"],\"TcpServerSocketEvent\":[\"Event\"],\"TcpServerSocketEventInit\":[],\"TcpSocket\":[\"EventTarget\"],\"TcpSocketBinaryType\":[],\"TcpSocketErrorEvent\":[\"Event\"],\"TcpSocketErrorEventInit\":[],\"TcpSocketEvent\":[\"Event\"],\"TcpSocketEventInit\":[],\"Text\":[\"CharacterData\",\"EventTarget\",\"Node\"],\"TextDecodeOptions\":[],\"TextDecoder\":[],\"TextDecoderOptions\":[],\"TextEncoder\":[],\"TextMetrics\":[],\"TextTrack\":[\"EventTarget\"],\"TextTrackCue\":[\"EventTarget\"],\"TextTrackCueList\":[],\"TextTrackKind\":[],\"TextTrackList\":[\"EventTarget\"],\"TextTrackMode\":[],\"TimeEvent\":[\"Event\"],\"TimeRanges\":[],\"ToggleEvent\":[\"Event\"],\"ToggleEventInit\":[],\"TokenBinding\":[],\"TokenBindingStatus\":[],\"Touch\":[],\"TouchEvent\":[\"Event\",\"UiEvent\"],\"TouchEventInit\":[],\"TouchInit\":[],\"TouchList\":[],\"TrackEvent\":[\"Event\"],\"TrackEventInit\":[],\"TransformStream\":[],\"TransformStreamDefaultController\":[],\"Transformer\":[],\"TransitionEvent\":[\"Event\"],\"TransitionEventInit\":[],\"Transport\":[],\"TreeBoxObject\":[],\"TreeCellInfo\":[],\"TreeView\":[],\"TreeWalker\":[],\"U2f\":[],\"U2fClientData\":[],\"ULongRange\":[],\"UaDataValues\":[],\"UaLowEntropyJson\":[],\"UdpMessageEventInit\":[],\"UdpOptions\":[],\"UiEvent\":[\"Event\"],\"UiEventInit\":[],\"UnderlyingSink\":[],\"UnderlyingSource\":[],\"Url\":[],\"UrlSearchParams\":[],\"Usb\":[\"EventTarget\"],\"UsbAlternateInterface\":[],\"UsbConfiguration\":[],\"UsbConnectionEvent\":[\"Event\"],\"UsbConnectionEventInit\":[],\"UsbControlTransferParameters\":[],\"UsbDevice\":[],\"UsbDeviceFilter\":[],\"UsbDeviceRequestOptions\":[],\"UsbDirection\":[],\"UsbEndpoint\":[],\"UsbEndpointType\":[],\"UsbInTransferResult\":[],\"UsbInterface\":[],\"UsbIsochronousInTransferPacket\":[],\"UsbIsochronousInTransferResult\":[],\"UsbIsochronousOutTransferPacket\":[],\"UsbIsochronousOutTransferResult\":[],\"UsbOutTransferResult\":[],\"UsbPermissionDescriptor\":[],\"UsbPermissionResult\":[\"EventTarget\",\"PermissionStatus\"],\"UsbPermissionStorage\":[],\"UsbRecipient\":[],\"UsbRequestType\":[],\"UsbTransferStatus\":[],\"UserActivation\":[],\"UserProximityEvent\":[\"Event\"],\"UserProximityEventInit\":[],\"UserVerificationRequirement\":[],\"ValidityState\":[],\"ValueEvent\":[\"Event\"],\"ValueEventInit\":[],\"VideoColorPrimaries\":[],\"VideoColorSpace\":[],\"VideoColorSpaceInit\":[],\"VideoConfiguration\":[],\"VideoDecoder\":[],\"VideoDecoderConfig\":[],\"VideoDecoderInit\":[],\"VideoDecoderSupport\":[],\"VideoEncoder\":[],\"VideoEncoderConfig\":[],\"VideoEncoderEncodeOptions\":[],\"VideoEncoderInit\":[],\"VideoEncoderSupport\":[],\"VideoFacingModeEnum\":[],\"VideoFrame\":[],\"VideoFrameBufferInit\":[],\"VideoFrameCopyToOptions\":[],\"VideoFrameInit\":[],\"VideoMatrixCoefficients\":[],\"VideoPixelFormat\":[],\"VideoPlaybackQuality\":[],\"VideoStreamTrack\":[\"EventTarget\",\"MediaStreamTrack\"],\"VideoTrack\":[],\"VideoTrackList\":[\"EventTarget\"],\"VideoTransferCharacteristics\":[],\"ViewTransition\":[],\"VisibilityState\":[],\"VisualViewport\":[\"EventTarget\"],\"VoidCallback\":[],\"VrDisplay\":[\"EventTarget\"],\"VrDisplayCapabilities\":[],\"VrEye\":[],\"VrEyeParameters\":[],\"VrFieldOfView\":[],\"VrFrameData\":[],\"VrLayer\":[],\"VrMockController\":[],\"VrMockDisplay\":[],\"VrPose\":[],\"VrServiceTest\":[],\"VrStageParameters\":[],\"VrSubmitFrameResult\":[],\"VttCue\":[\"EventTarget\",\"TextTrackCue\"],\"VttRegion\":[],\"WakeLock\":[],\"WakeLockSentinel\":[\"EventTarget\"],\"WakeLockType\":[],\"WatchAdvertisementsOptions\":[],\"WaveShaperNode\":[\"AudioNode\",\"EventTarget\"],\"WaveShaperOptions\":[],\"WebGl2RenderingContext\":[],\"WebGlActiveInfo\":[],\"WebGlBuffer\":[],\"WebGlContextAttributes\":[],\"WebGlContextEvent\":[\"Event\"],\"WebGlContextEventInit\":[],\"WebGlFramebuffer\":[],\"WebGlPowerPreference\":[],\"WebGlProgram\":[],\"WebGlQuery\":[],\"WebGlRenderbuffer\":[],\"WebGlRenderingContext\":[],\"WebGlSampler\":[],\"WebGlShader\":[],\"WebGlShaderPrecisionFormat\":[],\"WebGlSync\":[],\"WebGlTexture\":[],\"WebGlTransformFeedback\":[],\"WebGlUniformLocation\":[],\"WebGlVertexArrayObject\":[],\"WebKitCssMatrix\":[\"DomMatrix\",\"DomMatrixReadOnly\"],\"WebSocket\":[\"EventTarget\"],\"WebSocketDict\":[],\"WebSocketElement\":[],\"WebTransport\":[],\"WebTransportBidirectionalStream\":[],\"WebTransportCloseInfo\":[],\"WebTransportCongestionControl\":[],\"WebTransportDatagramDuplexStream\":[],\"WebTransportDatagramStats\":[],\"WebTransportError\":[\"DomException\"],\"WebTransportErrorOptions\":[],\"WebTransportErrorSource\":[],\"WebTransportHash\":[],\"WebTransportOptions\":[],\"WebTransportReceiveStream\":[\"ReadableStream\"],\"WebTransportReceiveStreamStats\":[],\"WebTransportReliabilityMode\":[],\"WebTransportSendStream\":[\"WritableStream\"],\"WebTransportSendStreamOptions\":[],\"WebTransportSendStreamStats\":[],\"WebTransportStats\":[],\"WebglColorBufferFloat\":[],\"WebglCompressedTextureAstc\":[],\"WebglCompressedTextureAtc\":[],\"WebglCompressedTextureEtc\":[],\"WebglCompressedTextureEtc1\":[],\"WebglCompressedTexturePvrtc\":[],\"WebglCompressedTextureS3tc\":[],\"WebglCompressedTextureS3tcSrgb\":[],\"WebglDebugRendererInfo\":[],\"WebglDebugShaders\":[],\"WebglDepthTexture\":[],\"WebglDrawBuffers\":[],\"WebglLoseContext\":[],\"WebglMultiDraw\":[],\"WellKnownDirectory\":[],\"WgslLanguageFeatures\":[],\"WheelEvent\":[\"Event\",\"MouseEvent\",\"UiEvent\"],\"WheelEventInit\":[],\"WidevineCdmManifest\":[],\"Window\":[\"EventTarget\"],\"WindowClient\":[\"Client\"],\"Worker\":[\"EventTarget\"],\"WorkerDebuggerGlobalScope\":[\"EventTarget\"],\"WorkerGlobalScope\":[\"EventTarget\"],\"WorkerLocation\":[],\"WorkerNavigator\":[],\"WorkerOptions\":[],\"WorkerType\":[],\"Worklet\":[],\"WorkletGlobalScope\":[],\"WorkletOptions\":[],\"WritableStream\":[],\"WritableStreamDefaultController\":[],\"WritableStreamDefaultWriter\":[],\"WriteCommandType\":[],\"WriteParams\":[],\"XPathExpression\":[],\"XPathNsResolver\":[],\"XPathResult\":[],\"XmlDocument\":[\"Document\",\"EventTarget\",\"Node\"],\"XmlHttpRequest\":[\"EventTarget\",\"XmlHttpRequestEventTarget\"],\"XmlHttpRequestEventTarget\":[\"EventTarget\"],\"XmlHttpRequestResponseType\":[],\"XmlHttpRequestUpload\":[\"EventTarget\",\"XmlHttpRequestEventTarget\"],\"XmlSerializer\":[],\"XrBoundedReferenceSpace\":[\"EventTarget\",\"XrReferenceSpace\",\"XrSpace\"],\"XrEye\":[],\"XrFrame\":[],\"XrHand\":[],\"XrHandJoint\":[],\"XrHandedness\":[],\"XrInputSource\":[],\"XrInputSourceArray\":[],\"XrInputSourceEvent\":[\"Event\"],\"XrInputSourceEventInit\":[],\"XrInputSourcesChangeEvent\":[\"Event\"],\"XrInputSourcesChangeEventInit\":[],\"XrJointPose\":[\"XrPose\"],\"XrJointSpace\":[\"EventTarget\",\"XrSpace\"],\"XrLayer\":[\"EventTarget\"],\"XrPermissionDescriptor\":[],\"XrPermissionStatus\":[\"EventTarget\",\"PermissionStatus\"],\"XrPose\":[],\"XrReferenceSpace\":[\"EventTarget\",\"XrSpace\"],\"XrReferenceSpaceEvent\":[\"Event\"],\"XrReferenceSpaceEventInit\":[],\"XrReferenceSpaceType\":[],\"XrRenderState\":[],\"XrRenderStateInit\":[],\"XrRigidTransform\":[],\"XrSession\":[\"EventTarget\"],\"XrSessionEvent\":[\"Event\"],\"XrSessionEventInit\":[],\"XrSessionInit\":[],\"XrSessionMode\":[],\"XrSessionSupportedPermissionDescriptor\":[],\"XrSpace\":[\"EventTarget\"],\"XrSystem\":[\"EventTarget\"],\"XrTargetRayMode\":[],\"XrView\":[],\"XrViewerPose\":[\"XrPose\"],\"XrViewport\":[],\"XrVisibilityState\":[],\"XrWebGlLayer\":[\"EventTarget\",\"XrLayer\"],\"XrWebGlLayerInit\":[],\"XsltProcessor\":[],\"console\":[],\"css\":[],\"default\":[\"std\"],\"gpu_buffer_usage\":[],\"gpu_color_write\":[],\"gpu_map_mode\":[],\"gpu_shader_stage\":[],\"gpu_texture_usage\":[],\"std\":[\"wasm-bindgen/std\",\"js-sys/std\"]}}", "web-time_1.1.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"futures-channel\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_feature = \\\"atomics\\\"))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_feature = \\\"atomics\\\"))\"},{\"features\":[\"js\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.2\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"name\":\"js-sys\",\"req\":\"^0.3.20\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"features\":[\"macro\"],\"kind\":\"dev\",\"name\":\"pollster\",\"req\":\"^0.3\",\"target\":\"cfg(not(target_family = \\\"wasm\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"^0.2.70\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-futures\",\"req\":\"^0.4\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"},{\"features\":[\"WorkerGlobalScope\"],\"kind\":\"dev\",\"name\":\"web-sys\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_feature = \\\"atomics\\\"))\"},{\"features\":[\"CssStyleDeclaration\",\"Document\",\"Element\",\"HtmlTableElement\",\"HtmlTableRowElement\",\"Performance\",\"Window\"],\"kind\":\"dev\",\"name\":\"web-sys\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"serde\":[\"dep:serde\"]}}", "webbrowser_1.0.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"actix-files\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"actix-web\",\"req\":\"^4\"},{\"name\":\"core-foundation\",\"req\":\"^0.10\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"kind\":\"dev\",\"name\":\"crossbeam-channel\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.9.0\"},{\"name\":\"jni\",\"req\":\"^0.21\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"ndk-context\",\"req\":\"^0.1\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"kind\":\"dev\",\"name\":\"ndk-glue\",\"req\":\">=0.3, <=0.7\",\"target\":\"cfg(target_os = \\\"android\\\")\"},{\"name\":\"objc2\",\"req\":\"^0.6\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"tvos\\\", target_os = \\\"visionos\\\"))\"},{\"default_features\":false,\"features\":[\"std\",\"NSDictionary\",\"NSString\",\"NSURL\"],\"name\":\"objc2-foundation\",\"req\":\"^0.3\",\"target\":\"cfg(any(target_os = \\\"ios\\\", target_os = \\\"tvos\\\", target_os = \\\"visionos\\\"))\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"serial_test\",\"req\":\"^0.10\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"url\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"urlencoding\",\"req\":\"^2.1\"},{\"features\":[\"Window\"],\"name\":\"web-sys\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"disable-wsl\":[],\"hardened\":[],\"wasm-console\":[\"web-sys/console\"]}}", - "webpki-root-certs_1.0.4": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"}],\"features\":{}}", + "webpki-root-certs_1.0.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"}],\"features\":{}}", "webpki-roots_0.26.11": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"parent\",\"package\":\"webpki-roots\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.13\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.102\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"yasna\",\"req\":\"^0.5.2\"}],\"features\":{}}", - "webpki-roots_1.0.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"yasna\",\"req\":\"^0.5.2\"}],\"features\":{}}", - "weezl_0.1.10": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.12\"},{\"default_features\":false,\"features\":[\"macros\",\"io-util\",\"net\",\"rt\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"compat\"],\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.6.2\"}],\"features\":{\"alloc\":[],\"async\":[\"futures\",\"std\"],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", + "webpki-roots_1.0.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14.3\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"yasna\",\"req\":\"^0.5.2\"}],\"features\":{}}", + "weezl_0.1.12": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.12\"},{\"default_features\":false,\"features\":[\"macros\",\"io-util\",\"net\",\"rt\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"compat\"],\"kind\":\"dev\",\"name\":\"tokio-util\",\"req\":\"^0.6.2\"}],\"features\":{\"alloc\":[],\"async\":[\"futures\",\"std\"],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "which_8.0.0": "{\"dependencies\":[{\"name\":\"env_home\",\"optional\":true,\"req\":\"^0.1.0\",\"target\":\"cfg(any(windows, unix, target_os = \\\"redox\\\"))\"},{\"name\":\"regex\",\"optional\":true,\"req\":\"^1.10.2\"},{\"default_features\":false,\"features\":[\"fs\",\"std\"],\"name\":\"rustix\",\"optional\":true,\"req\":\"^1.0.5\",\"target\":\"cfg(any(unix, target_os = \\\"wasi\\\", target_os = \\\"redox\\\"))\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.9.0\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.40\"},{\"features\":[\"kernel\"],\"name\":\"winsafe\",\"optional\":true,\"req\":\"^0.0.19\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[\"real-sys\"],\"real-sys\":[\"dep:env_home\",\"dep:rustix\",\"dep:winsafe\"],\"regex\":[\"dep:regex\"],\"tracing\":[\"dep:tracing\"]}}", "whoami_1.6.1": "{\"dependencies\":[{\"name\":\"libredox\",\"req\":\"^0.1.1\",\"target\":\"cfg(all(target_os = \\\"redox\\\", not(target_arch = \\\"wasm32\\\")))\"},{\"name\":\"wasite\",\"req\":\"^0.1\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"wasi\\\"))\"},{\"features\":[\"Navigator\",\"Document\",\"Window\",\"Location\"],\"name\":\"web-sys\",\"optional\":true,\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(target_os = \\\"wasi\\\"), not(daku)))\"}],\"features\":{\"default\":[\"web\"],\"web\":[\"web-sys\"]}}", "widestring_1.2.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"debugger_test\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"debugger_test_parser\",\"req\":\"^0.1\"},{\"features\":[\"Win32_System_Diagnostics_Debug\"],\"kind\":\"dev\",\"name\":\"windows-sys\",\"req\":\"^0.59\"}],\"features\":{\"alloc\":[],\"debugger_visualizer\":[\"alloc\"],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "wildcard_0.3.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.10.5\"},{\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.203\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2.0.3\"},{\"kind\":\"dev\",\"name\":\"toml\",\"req\":\"^0.8.14\"},{\"kind\":\"dev\",\"name\":\"wildmatch\",\"req\":\"^2.3.4\"}],\"features\":{\"fatal-warnings\":[]}}", "wildmatch_2.6.1": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"glob\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"ntest\",\"req\":\"^0.9.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.10.2\"},{\"kind\":\"dev\",\"name\":\"regex-lite\",\"req\":\"^0.1.5\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"serde\":[\"dep:serde\"]}}", "winapi-i686-pc-windows-gnu_0.4.0": "{\"dependencies\":[],\"features\":{}}", - "winapi-util_0.1.9": "{\"dependencies\":[{\"features\":[\"Win32_Foundation\",\"Win32_Storage_FileSystem\",\"Win32_System_Console\",\"Win32_System_SystemInformation\"],\"name\":\"windows-sys\",\"req\":\">=0.48.0, <=0.59\",\"target\":\"cfg(windows)\"}],\"features\":{}}", + "winapi-util_0.1.11": "{\"dependencies\":[{\"features\":[\"Win32_Foundation\",\"Win32_Storage_FileSystem\",\"Win32_System_Console\",\"Win32_System_SystemInformation\"],\"name\":\"windows-sys\",\"req\":\">=0.48.0, <=0.61\",\"target\":\"cfg(windows)\"}],\"features\":{}}", "winapi-x86_64-pc-windows-gnu_0.4.0": "{\"dependencies\":[],\"features\":{}}", "winapi_0.3.9": "{\"dependencies\":[{\"name\":\"winapi-i686-pc-windows-gnu\",\"req\":\"^0.4\",\"target\":\"i686-pc-windows-gnu\"},{\"name\":\"winapi-x86_64-pc-windows-gnu\",\"req\":\"^0.4\",\"target\":\"x86_64-pc-windows-gnu\"}],\"features\":{\"accctrl\":[],\"aclapi\":[],\"activation\":[],\"adhoc\":[],\"appmgmt\":[],\"audioclient\":[],\"audiosessiontypes\":[],\"avrt\":[],\"basetsd\":[],\"bcrypt\":[],\"bits\":[],\"bits10_1\":[],\"bits1_5\":[],\"bits2_0\":[],\"bits2_5\":[],\"bits3_0\":[],\"bits4_0\":[],\"bits5_0\":[],\"bitscfg\":[],\"bitsmsg\":[],\"bluetoothapis\":[],\"bluetoothleapis\":[],\"bthdef\":[],\"bthioctl\":[],\"bthledef\":[],\"bthsdpdef\":[],\"bugcodes\":[],\"cderr\":[],\"cfg\":[],\"cfgmgr32\":[],\"cguid\":[],\"combaseapi\":[],\"coml2api\":[],\"commapi\":[],\"commctrl\":[],\"commdlg\":[],\"commoncontrols\":[],\"consoleapi\":[],\"corecrt\":[],\"corsym\":[],\"d2d1\":[],\"d2d1_1\":[],\"d2d1_2\":[],\"d2d1_3\":[],\"d2d1effectauthor\":[],\"d2d1effects\":[],\"d2d1effects_1\":[],\"d2d1effects_2\":[],\"d2d1svg\":[],\"d2dbasetypes\":[],\"d3d\":[],\"d3d10\":[],\"d3d10_1\":[],\"d3d10_1shader\":[],\"d3d10effect\":[],\"d3d10misc\":[],\"d3d10sdklayers\":[],\"d3d10shader\":[],\"d3d11\":[],\"d3d11_1\":[],\"d3d11_2\":[],\"d3d11_3\":[],\"d3d11_4\":[],\"d3d11on12\":[],\"d3d11sdklayers\":[],\"d3d11shader\":[],\"d3d11tokenizedprogramformat\":[],\"d3d12\":[],\"d3d12sdklayers\":[],\"d3d12shader\":[],\"d3d9\":[],\"d3d9caps\":[],\"d3d9types\":[],\"d3dcommon\":[],\"d3dcompiler\":[],\"d3dcsx\":[],\"d3dkmdt\":[],\"d3dkmthk\":[],\"d3dukmdt\":[],\"d3dx10core\":[],\"d3dx10math\":[],\"d3dx10mesh\":[],\"datetimeapi\":[],\"davclnt\":[],\"dbghelp\":[],\"dbt\":[],\"dcommon\":[],\"dcomp\":[],\"dcompanimation\":[],\"dcomptypes\":[],\"dde\":[],\"ddraw\":[],\"ddrawi\":[],\"ddrawint\":[],\"debug\":[\"impl-debug\"],\"debugapi\":[],\"devguid\":[],\"devicetopology\":[],\"devpkey\":[],\"devpropdef\":[],\"dinput\":[],\"dinputd\":[],\"dispex\":[],\"dmksctl\":[],\"dmusicc\":[],\"docobj\":[],\"documenttarget\":[],\"dot1x\":[],\"dpa_dsa\":[],\"dpapi\":[],\"dsgetdc\":[],\"dsound\":[],\"dsrole\":[],\"dvp\":[],\"dwmapi\":[],\"dwrite\":[],\"dwrite_1\":[],\"dwrite_2\":[],\"dwrite_3\":[],\"dxdiag\":[],\"dxfile\":[],\"dxgi\":[],\"dxgi1_2\":[],\"dxgi1_3\":[],\"dxgi1_4\":[],\"dxgi1_5\":[],\"dxgi1_6\":[],\"dxgidebug\":[],\"dxgiformat\":[],\"dxgitype\":[],\"dxva2api\":[],\"dxvahd\":[],\"eaptypes\":[],\"enclaveapi\":[],\"endpointvolume\":[],\"errhandlingapi\":[],\"everything\":[],\"evntcons\":[],\"evntprov\":[],\"evntrace\":[],\"excpt\":[],\"exdisp\":[],\"fibersapi\":[],\"fileapi\":[],\"functiondiscoverykeys_devpkey\":[],\"gl-gl\":[],\"guiddef\":[],\"handleapi\":[],\"heapapi\":[],\"hidclass\":[],\"hidpi\":[],\"hidsdi\":[],\"hidusage\":[],\"highlevelmonitorconfigurationapi\":[],\"hstring\":[],\"http\":[],\"ifdef\":[],\"ifmib\":[],\"imm\":[],\"impl-debug\":[],\"impl-default\":[],\"in6addr\":[],\"inaddr\":[],\"inspectable\":[],\"interlockedapi\":[],\"intsafe\":[],\"ioapiset\":[],\"ipexport\":[],\"iphlpapi\":[],\"ipifcons\":[],\"ipmib\":[],\"iprtrmib\":[],\"iptypes\":[],\"jobapi\":[],\"jobapi2\":[],\"knownfolders\":[],\"ks\":[],\"ksmedia\":[],\"ktmtypes\":[],\"ktmw32\":[],\"l2cmn\":[],\"libloaderapi\":[],\"limits\":[],\"lmaccess\":[],\"lmalert\":[],\"lmapibuf\":[],\"lmat\":[],\"lmcons\":[],\"lmdfs\":[],\"lmerrlog\":[],\"lmjoin\":[],\"lmmsg\":[],\"lmremutl\":[],\"lmrepl\":[],\"lmserver\":[],\"lmshare\":[],\"lmstats\":[],\"lmsvc\":[],\"lmuse\":[],\"lmwksta\":[],\"lowlevelmonitorconfigurationapi\":[],\"lsalookup\":[],\"memoryapi\":[],\"minschannel\":[],\"minwinbase\":[],\"minwindef\":[],\"mmdeviceapi\":[],\"mmeapi\":[],\"mmreg\":[],\"mmsystem\":[],\"mprapidef\":[],\"msaatext\":[],\"mscat\":[],\"mschapp\":[],\"mssip\":[],\"mstcpip\":[],\"mswsock\":[],\"mswsockdef\":[],\"namedpipeapi\":[],\"namespaceapi\":[],\"nb30\":[],\"ncrypt\":[],\"netioapi\":[],\"nldef\":[],\"ntddndis\":[],\"ntddscsi\":[],\"ntddser\":[],\"ntdef\":[],\"ntlsa\":[],\"ntsecapi\":[],\"ntstatus\":[],\"oaidl\":[],\"objbase\":[],\"objidl\":[],\"objidlbase\":[],\"ocidl\":[],\"ole2\":[],\"oleauto\":[],\"olectl\":[],\"oleidl\":[],\"opmapi\":[],\"pdh\":[],\"perflib\":[],\"physicalmonitorenumerationapi\":[],\"playsoundapi\":[],\"portabledevice\":[],\"portabledeviceapi\":[],\"portabledevicetypes\":[],\"powerbase\":[],\"powersetting\":[],\"powrprof\":[],\"processenv\":[],\"processsnapshot\":[],\"processthreadsapi\":[],\"processtopologyapi\":[],\"profileapi\":[],\"propidl\":[],\"propkey\":[],\"propkeydef\":[],\"propsys\":[],\"prsht\":[],\"psapi\":[],\"qos\":[],\"realtimeapiset\":[],\"reason\":[],\"restartmanager\":[],\"restrictederrorinfo\":[],\"rmxfguid\":[],\"roapi\":[],\"robuffer\":[],\"roerrorapi\":[],\"rpc\":[],\"rpcdce\":[],\"rpcndr\":[],\"rtinfo\":[],\"sapi\":[],\"sapi51\":[],\"sapi53\":[],\"sapiddk\":[],\"sapiddk51\":[],\"schannel\":[],\"sddl\":[],\"securityappcontainer\":[],\"securitybaseapi\":[],\"servprov\":[],\"setupapi\":[],\"shellapi\":[],\"shellscalingapi\":[],\"shlobj\":[],\"shobjidl\":[],\"shobjidl_core\":[],\"shtypes\":[],\"softpub\":[],\"spapidef\":[],\"spellcheck\":[],\"sporder\":[],\"sql\":[],\"sqlext\":[],\"sqltypes\":[],\"sqlucode\":[],\"sspi\":[],\"std\":[],\"stralign\":[],\"stringapiset\":[],\"strmif\":[],\"subauth\":[],\"synchapi\":[],\"sysinfoapi\":[],\"systemtopologyapi\":[],\"taskschd\":[],\"tcpestats\":[],\"tcpmib\":[],\"textstor\":[],\"threadpoolapiset\":[],\"threadpoollegacyapiset\":[],\"timeapi\":[],\"timezoneapi\":[],\"tlhelp32\":[],\"transportsettingcommon\":[],\"tvout\":[],\"udpmib\":[],\"unknwnbase\":[],\"urlhist\":[],\"urlmon\":[],\"usb\":[],\"usbioctl\":[],\"usbiodef\":[],\"usbscan\":[],\"usbspec\":[],\"userenv\":[],\"usp10\":[],\"utilapiset\":[],\"uxtheme\":[],\"vadefs\":[],\"vcruntime\":[],\"vsbackup\":[],\"vss\":[],\"vsserror\":[],\"vswriter\":[],\"wbemads\":[],\"wbemcli\":[],\"wbemdisp\":[],\"wbemprov\":[],\"wbemtran\":[],\"wct\":[],\"werapi\":[],\"winbase\":[],\"wincodec\":[],\"wincodecsdk\":[],\"wincon\":[],\"wincontypes\":[],\"wincred\":[],\"wincrypt\":[],\"windef\":[],\"windot11\":[],\"windowsceip\":[],\"windowsx\":[],\"winefs\":[],\"winerror\":[],\"winevt\":[],\"wingdi\":[],\"winhttp\":[],\"wininet\":[],\"winineti\":[],\"winioctl\":[],\"winnetwk\":[],\"winnls\":[],\"winnt\":[],\"winreg\":[],\"winsafer\":[],\"winscard\":[],\"winsmcrd\":[],\"winsock2\":[],\"winspool\":[],\"winstring\":[],\"winsvc\":[],\"wintrust\":[],\"winusb\":[],\"winusbio\":[],\"winuser\":[],\"winver\":[],\"wlanapi\":[],\"wlanihv\":[],\"wlanihvtypes\":[],\"wlantypes\":[],\"wlclient\":[],\"wmistr\":[],\"wnnc\":[],\"wow64apiset\":[],\"wpdmtpextensions\":[],\"ws2bth\":[],\"ws2def\":[],\"ws2ipdef\":[],\"ws2spi\":[],\"ws2tcpip\":[],\"wtsapi32\":[],\"wtypes\":[],\"wtypesbase\":[],\"xinput\":[]}}", - "windows-collections_0.2.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-core\",\"req\":\"^0.61.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"windows-result\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"windows-strings\",\"req\":\"^0.4.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "windows-collections_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-core\",\"req\":\"^0.62.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"windows-strings\",\"req\":\"^0.5.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"windows-core/std\"]}}", "windows-core_0.58.0": "{\"dependencies\":[{\"name\":\"windows-implement\",\"req\":\"^0.58.0\"},{\"name\":\"windows-interface\",\"req\":\"^0.58.0\"},{\"name\":\"windows-result\",\"req\":\"^0.2.0\"},{\"name\":\"windows-strings\",\"req\":\"^0.1.0\"},{\"name\":\"windows-targets\",\"req\":\"^0.52.6\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", - "windows-core_0.61.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-implement\",\"req\":\"^0.60.0\"},{\"default_features\":false,\"name\":\"windows-interface\",\"req\":\"^0.59.1\"},{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.1.1\"},{\"default_features\":false,\"name\":\"windows-result\",\"req\":\"^0.3.4\"},{\"default_features\":false,\"name\":\"windows-strings\",\"req\":\"^0.4.2\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"windows-result/std\",\"windows-strings/std\"]}}", - "windows-future_0.2.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-core\",\"req\":\"^0.61.1\"},{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.1.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"windows-result\",\"req\":\"^0.3.3\"},{\"default_features\":false,\"name\":\"windows-threading\",\"req\":\"^0.1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "windows-core_0.62.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-implement\",\"req\":\"^0.60.2\"},{\"default_features\":false,\"name\":\"windows-interface\",\"req\":\"^0.59.3\"},{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"name\":\"windows-result\",\"req\":\"^0.4.1\"},{\"default_features\":false,\"name\":\"windows-strings\",\"req\":\"^0.5.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"windows-result/std\",\"windows-strings/std\"]}}", + "windows-future_0.3.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-core\",\"req\":\"^0.62.2\"},{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"name\":\"windows-threading\",\"req\":\"^0.2.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"windows-core/std\"]}}", "windows-implement_0.58.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"parsing\",\"proc-macro\",\"printing\",\"full\",\"derive\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", - "windows-implement_0.60.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"parsing\",\"proc-macro\",\"printing\",\"full\",\"clone-impls\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", + "windows-implement_0.60.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"parsing\",\"proc-macro\",\"printing\",\"full\",\"clone-impls\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", "windows-interface_0.58.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"parsing\",\"proc-macro\",\"printing\",\"full\",\"derive\",\"clone-impls\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", - "windows-interface_0.59.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"parsing\",\"proc-macro\",\"printing\",\"full\",\"clone-impls\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", - "windows-link_0.1.3": "{\"dependencies\":[],\"features\":{}}", - "windows-link_0.2.0": "{\"dependencies\":[],\"features\":{}}", - "windows-numerics_0.2.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-core\",\"req\":\"^0.61.0\"},{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.1.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", - "windows-registry_0.5.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.1.3\"},{\"default_features\":false,\"name\":\"windows-result\",\"req\":\"^0.3.4\"},{\"default_features\":false,\"name\":\"windows-strings\",\"req\":\"^0.4.2\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"windows-result/std\",\"windows-strings/std\"]}}", + "windows-interface_0.59.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"parsing\",\"proc-macro\",\"printing\",\"full\",\"clone-impls\"],\"name\":\"syn\",\"req\":\"^2.0\"}],\"features\":{}}", + "windows-link_0.2.1": "{\"dependencies\":[],\"features\":{}}", + "windows-numerics_0.3.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-core\",\"req\":\"^0.62.2\"},{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.2.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"windows-core/std\"]}}", + "windows-registry_0.6.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"name\":\"windows-result\",\"req\":\"^0.4.1\"},{\"default_features\":false,\"name\":\"windows-strings\",\"req\":\"^0.5.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"windows-result/std\",\"windows-strings/std\"]}}", "windows-result_0.2.0": "{\"dependencies\":[{\"name\":\"windows-targets\",\"req\":\"^0.52.6\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", - "windows-result_0.3.4": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.1.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "windows-result_0.4.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.2.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "windows-strings_0.1.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-result\",\"req\":\"^0.2.0\"},{\"name\":\"windows-targets\",\"req\":\"^0.52.6\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", - "windows-strings_0.4.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.1.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", + "windows-strings_0.5.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.2.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "windows-sys_0.45.0": "{\"dependencies\":[{\"name\":\"windows-targets\",\"req\":\"^0.42.1\",\"target\":\"cfg(not(windows_raw_dylib))\"}],\"features\":{\"Win32\":[],\"Win32_Data\":[\"Win32\"],\"Win32_Data_HtmlHelp\":[\"Win32_Data\"],\"Win32_Data_RightsManagement\":[\"Win32_Data\"],\"Win32_Data_Xml\":[\"Win32_Data\"],\"Win32_Data_Xml_MsXml\":[\"Win32_Data_Xml\"],\"Win32_Data_Xml_XmlLite\":[\"Win32_Data_Xml\"],\"Win32_Devices\":[\"Win32\"],\"Win32_Devices_AllJoyn\":[\"Win32_Devices\"],\"Win32_Devices_BiometricFramework\":[\"Win32_Devices\"],\"Win32_Devices_Bluetooth\":[\"Win32_Devices\"],\"Win32_Devices_Communication\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAccess\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAndDriverInstallation\":[\"Win32_Devices\"],\"Win32_Devices_DeviceQuery\":[\"Win32_Devices\"],\"Win32_Devices_Display\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration_Pnp\":[\"Win32_Devices_Enumeration\"],\"Win32_Devices_Fax\":[\"Win32_Devices\"],\"Win32_Devices_FunctionDiscovery\":[\"Win32_Devices\"],\"Win32_Devices_Geolocation\":[\"Win32_Devices\"],\"Win32_Devices_HumanInterfaceDevice\":[\"Win32_Devices\"],\"Win32_Devices_ImageAcquisition\":[\"Win32_Devices\"],\"Win32_Devices_PortableDevices\":[\"Win32_Devices\"],\"Win32_Devices_Properties\":[\"Win32_Devices\"],\"Win32_Devices_Pwm\":[\"Win32_Devices\"],\"Win32_Devices_Sensors\":[\"Win32_Devices\"],\"Win32_Devices_SerialCommunication\":[\"Win32_Devices\"],\"Win32_Devices_Tapi\":[\"Win32_Devices\"],\"Win32_Devices_Usb\":[\"Win32_Devices\"],\"Win32_Devices_WebServicesOnDevices\":[\"Win32_Devices\"],\"Win32_Foundation\":[\"Win32\"],\"Win32_Gaming\":[\"Win32\"],\"Win32_Globalization\":[\"Win32\"],\"Win32_Graphics\":[\"Win32\"],\"Win32_Graphics_Dwm\":[\"Win32_Graphics\"],\"Win32_Graphics_Gdi\":[\"Win32_Graphics\"],\"Win32_Graphics_Hlsl\":[\"Win32_Graphics\"],\"Win32_Graphics_OpenGL\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing_PrintTicket\":[\"Win32_Graphics_Printing\"],\"Win32_Management\":[\"Win32\"],\"Win32_Management_MobileDeviceManagementRegistration\":[\"Win32_Management\"],\"Win32_Media\":[\"Win32\"],\"Win32_Media_Audio\":[\"Win32_Media\"],\"Win32_Media_Audio_Apo\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_DirectMusic\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_Endpoints\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_XAudio2\":[\"Win32_Media_Audio\"],\"Win32_Media_DeviceManager\":[\"Win32_Media\"],\"Win32_Media_DxMediaObjects\":[\"Win32_Media\"],\"Win32_Media_KernelStreaming\":[\"Win32_Media\"],\"Win32_Media_LibrarySharingServices\":[\"Win32_Media\"],\"Win32_Media_MediaPlayer\":[\"Win32_Media\"],\"Win32_Media_Multimedia\":[\"Win32_Media\"],\"Win32_Media_Speech\":[\"Win32_Media\"],\"Win32_Media_Streaming\":[\"Win32_Media\"],\"Win32_Media_WindowsMediaFormat\":[\"Win32_Media\"],\"Win32_NetworkManagement\":[\"Win32\"],\"Win32_NetworkManagement_Dhcp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Dns\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_InternetConnectionWizard\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_IpHelper\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_MobileBroadband\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Multicast\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Ndis\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetBios\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetManagement\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetShell\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkDiagnosticsFramework\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkPolicyServer\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_P2P\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_QoS\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Rras\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Snmp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WNet\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WebDav\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WiFi\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectNow\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectionManager\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFilteringPlatform\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFirewall\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsNetworkVirtualization\":[\"Win32_NetworkManagement\"],\"Win32_Networking\":[\"Win32\"],\"Win32_Networking_ActiveDirectory\":[\"Win32_Networking\"],\"Win32_Networking_BackgroundIntelligentTransferService\":[\"Win32_Networking\"],\"Win32_Networking_Clustering\":[\"Win32_Networking\"],\"Win32_Networking_HttpServer\":[\"Win32_Networking\"],\"Win32_Networking_Ldap\":[\"Win32_Networking\"],\"Win32_Networking_NetworkListManager\":[\"Win32_Networking\"],\"Win32_Networking_RemoteDifferentialCompression\":[\"Win32_Networking\"],\"Win32_Networking_WebSocket\":[\"Win32_Networking\"],\"Win32_Networking_WinHttp\":[\"Win32_Networking\"],\"Win32_Networking_WinInet\":[\"Win32_Networking\"],\"Win32_Networking_WinSock\":[\"Win32_Networking\"],\"Win32_Networking_WindowsWebServices\":[\"Win32_Networking\"],\"Win32_Security\":[\"Win32\"],\"Win32_Security_AppLocker\":[\"Win32_Security\"],\"Win32_Security_Authentication\":[\"Win32_Security\"],\"Win32_Security_Authentication_Identity\":[\"Win32_Security_Authentication\"],\"Win32_Security_Authentication_Identity_Provider\":[\"Win32_Security_Authentication_Identity\"],\"Win32_Security_Authorization\":[\"Win32_Security\"],\"Win32_Security_Authorization_UI\":[\"Win32_Security_Authorization\"],\"Win32_Security_ConfigurationSnapin\":[\"Win32_Security\"],\"Win32_Security_Credentials\":[\"Win32_Security\"],\"Win32_Security_Cryptography\":[\"Win32_Security\"],\"Win32_Security_Cryptography_Catalog\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Certificates\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Sip\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_UI\":[\"Win32_Security_Cryptography\"],\"Win32_Security_DiagnosticDataQuery\":[\"Win32_Security\"],\"Win32_Security_DirectoryServices\":[\"Win32_Security\"],\"Win32_Security_EnterpriseData\":[\"Win32_Security\"],\"Win32_Security_ExtensibleAuthenticationProtocol\":[\"Win32_Security\"],\"Win32_Security_Isolation\":[\"Win32_Security\"],\"Win32_Security_LicenseProtection\":[\"Win32_Security\"],\"Win32_Security_NetworkAccessProtection\":[\"Win32_Security\"],\"Win32_Security_Tpm\":[\"Win32_Security\"],\"Win32_Security_WinTrust\":[\"Win32_Security\"],\"Win32_Security_WinWlx\":[\"Win32_Security\"],\"Win32_Storage\":[\"Win32\"],\"Win32_Storage_Cabinets\":[\"Win32_Storage\"],\"Win32_Storage_CloudFilters\":[\"Win32_Storage\"],\"Win32_Storage_Compression\":[\"Win32_Storage\"],\"Win32_Storage_DataDeduplication\":[\"Win32_Storage\"],\"Win32_Storage_DistributedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_EnhancedStorage\":[\"Win32_Storage\"],\"Win32_Storage_FileHistory\":[\"Win32_Storage\"],\"Win32_Storage_FileServerResourceManager\":[\"Win32_Storage\"],\"Win32_Storage_FileSystem\":[\"Win32_Storage\"],\"Win32_Storage_Imapi\":[\"Win32_Storage\"],\"Win32_Storage_IndexServer\":[\"Win32_Storage\"],\"Win32_Storage_InstallableFileSystems\":[\"Win32_Storage\"],\"Win32_Storage_IscsiDisc\":[\"Win32_Storage\"],\"Win32_Storage_Jet\":[\"Win32_Storage\"],\"Win32_Storage_OfflineFiles\":[\"Win32_Storage\"],\"Win32_Storage_OperationRecorder\":[\"Win32_Storage\"],\"Win32_Storage_Packaging\":[\"Win32_Storage\"],\"Win32_Storage_Packaging_Appx\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_Packaging_Opc\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_ProjectedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_StructuredStorage\":[\"Win32_Storage\"],\"Win32_Storage_Vhd\":[\"Win32_Storage\"],\"Win32_Storage_VirtualDiskService\":[\"Win32_Storage\"],\"Win32_Storage_Vss\":[\"Win32_Storage\"],\"Win32_Storage_Xps\":[\"Win32_Storage\"],\"Win32_Storage_Xps_Printing\":[\"Win32_Storage_Xps\"],\"Win32_System\":[\"Win32\"],\"Win32_System_AddressBook\":[\"Win32_System\"],\"Win32_System_Antimalware\":[\"Win32_System\"],\"Win32_System_ApplicationInstallationAndServicing\":[\"Win32_System\"],\"Win32_System_ApplicationVerifier\":[\"Win32_System\"],\"Win32_System_AssessmentTool\":[\"Win32_System\"],\"Win32_System_Com\":[\"Win32_System\"],\"Win32_System_Com_CallObj\":[\"Win32_System_Com\"],\"Win32_System_Com_ChannelCredentials\":[\"Win32_System_Com\"],\"Win32_System_Com_Events\":[\"Win32_System_Com\"],\"Win32_System_Com_Marshal\":[\"Win32_System_Com\"],\"Win32_System_Com_StructuredStorage\":[\"Win32_System_Com\"],\"Win32_System_Com_UI\":[\"Win32_System_Com\"],\"Win32_System_Com_Urlmon\":[\"Win32_System_Com\"],\"Win32_System_ComponentServices\":[\"Win32_System\"],\"Win32_System_Console\":[\"Win32_System\"],\"Win32_System_Contacts\":[\"Win32_System\"],\"Win32_System_CorrelationVector\":[\"Win32_System\"],\"Win32_System_DataExchange\":[\"Win32_System\"],\"Win32_System_DeploymentServices\":[\"Win32_System\"],\"Win32_System_DesktopSharing\":[\"Win32_System\"],\"Win32_System_DeveloperLicensing\":[\"Win32_System\"],\"Win32_System_Diagnostics\":[\"Win32_System\"],\"Win32_System_Diagnostics_Ceip\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Etw\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ProcessSnapshotting\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ToolHelp\":[\"Win32_System_Diagnostics\"],\"Win32_System_DistributedTransactionCoordinator\":[\"Win32_System\"],\"Win32_System_Environment\":[\"Win32_System\"],\"Win32_System_ErrorReporting\":[\"Win32_System\"],\"Win32_System_EventCollector\":[\"Win32_System\"],\"Win32_System_EventLog\":[\"Win32_System\"],\"Win32_System_EventNotificationService\":[\"Win32_System\"],\"Win32_System_GroupPolicy\":[\"Win32_System\"],\"Win32_System_HostCompute\":[\"Win32_System\"],\"Win32_System_HostComputeNetwork\":[\"Win32_System\"],\"Win32_System_HostComputeSystem\":[\"Win32_System\"],\"Win32_System_Hypervisor\":[\"Win32_System\"],\"Win32_System_IO\":[\"Win32_System\"],\"Win32_System_Iis\":[\"Win32_System\"],\"Win32_System_Ioctl\":[\"Win32_System\"],\"Win32_System_JobObjects\":[\"Win32_System\"],\"Win32_System_Js\":[\"Win32_System\"],\"Win32_System_Kernel\":[\"Win32_System\"],\"Win32_System_LibraryLoader\":[\"Win32_System\"],\"Win32_System_Mailslots\":[\"Win32_System\"],\"Win32_System_Mapi\":[\"Win32_System\"],\"Win32_System_Memory\":[\"Win32_System\"],\"Win32_System_Memory_NonVolatile\":[\"Win32_System_Memory\"],\"Win32_System_MessageQueuing\":[\"Win32_System\"],\"Win32_System_MixedReality\":[\"Win32_System\"],\"Win32_System_Mmc\":[\"Win32_System\"],\"Win32_System_Ole\":[\"Win32_System\"],\"Win32_System_ParentalControls\":[\"Win32_System\"],\"Win32_System_PasswordManagement\":[\"Win32_System\"],\"Win32_System_Performance\":[\"Win32_System\"],\"Win32_System_Performance_HardwareCounterProfiling\":[\"Win32_System_Performance\"],\"Win32_System_Pipes\":[\"Win32_System\"],\"Win32_System_Power\":[\"Win32_System\"],\"Win32_System_ProcessStatus\":[\"Win32_System\"],\"Win32_System_RealTimeCommunications\":[\"Win32_System\"],\"Win32_System_Recovery\":[\"Win32_System\"],\"Win32_System_Registry\":[\"Win32_System\"],\"Win32_System_RemoteAssistance\":[\"Win32_System\"],\"Win32_System_RemoteDesktop\":[\"Win32_System\"],\"Win32_System_RemoteManagement\":[\"Win32_System\"],\"Win32_System_RestartManager\":[\"Win32_System\"],\"Win32_System_Restore\":[\"Win32_System\"],\"Win32_System_Rpc\":[\"Win32_System\"],\"Win32_System_Search\":[\"Win32_System\"],\"Win32_System_Search_Common\":[\"Win32_System_Search\"],\"Win32_System_SecurityCenter\":[\"Win32_System\"],\"Win32_System_ServerBackup\":[\"Win32_System\"],\"Win32_System_Services\":[\"Win32_System\"],\"Win32_System_SettingsManagementInfrastructure\":[\"Win32_System\"],\"Win32_System_SetupAndMigration\":[\"Win32_System\"],\"Win32_System_Shutdown\":[\"Win32_System\"],\"Win32_System_StationsAndDesktops\":[\"Win32_System\"],\"Win32_System_SubsystemForLinux\":[\"Win32_System\"],\"Win32_System_SystemInformation\":[\"Win32_System\"],\"Win32_System_SystemServices\":[\"Win32_System\"],\"Win32_System_TaskScheduler\":[\"Win32_System\"],\"Win32_System_Threading\":[\"Win32_System\"],\"Win32_System_Time\":[\"Win32_System\"],\"Win32_System_TpmBaseServices\":[\"Win32_System\"],\"Win32_System_UpdateAgent\":[\"Win32_System\"],\"Win32_System_UpdateAssessment\":[\"Win32_System\"],\"Win32_System_UserAccessLogging\":[\"Win32_System\"],\"Win32_System_VirtualDosMachines\":[\"Win32_System\"],\"Win32_System_WindowsProgramming\":[\"Win32_System\"],\"Win32_System_WindowsSync\":[\"Win32_System\"],\"Win32_System_Wmi\":[\"Win32_System\"],\"Win32_UI\":[\"Win32\"],\"Win32_UI_Accessibility\":[\"Win32_UI\"],\"Win32_UI_Animation\":[\"Win32_UI\"],\"Win32_UI_ColorSystem\":[\"Win32_UI\"],\"Win32_UI_Controls\":[\"Win32_UI\"],\"Win32_UI_Controls_Dialogs\":[\"Win32_UI_Controls\"],\"Win32_UI_Controls_RichEdit\":[\"Win32_UI_Controls\"],\"Win32_UI_HiDpi\":[\"Win32_UI\"],\"Win32_UI_Input\":[\"Win32_UI\"],\"Win32_UI_Input_Ime\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Ink\":[\"Win32_UI_Input\"],\"Win32_UI_Input_KeyboardAndMouse\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Pointer\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Radial\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Touch\":[\"Win32_UI_Input\"],\"Win32_UI_Input_XboxController\":[\"Win32_UI_Input\"],\"Win32_UI_InteractionContext\":[\"Win32_UI\"],\"Win32_UI_LegacyWindowsEnvironmentFeatures\":[\"Win32_UI\"],\"Win32_UI_Magnification\":[\"Win32_UI\"],\"Win32_UI_Notifications\":[\"Win32_UI\"],\"Win32_UI_Ribbon\":[\"Win32_UI\"],\"Win32_UI_Shell\":[\"Win32_UI\"],\"Win32_UI_Shell_Common\":[\"Win32_UI_Shell\"],\"Win32_UI_Shell_PropertiesSystem\":[\"Win32_UI_Shell\"],\"Win32_UI_TabletPC\":[\"Win32_UI\"],\"Win32_UI_TextServices\":[\"Win32_UI\"],\"Win32_UI_WindowsAndMessaging\":[\"Win32_UI\"],\"Win32_UI_Wpf\":[\"Win32_UI\"],\"default\":[]}}", "windows-sys_0.48.0": "{\"dependencies\":[{\"name\":\"windows-targets\",\"req\":\"^0.48.0\"}],\"features\":{\"Wdk\":[],\"Wdk_System\":[\"Wdk\"],\"Wdk_System_OfflineRegistry\":[\"Wdk_System\"],\"Win32\":[],\"Win32_Data\":[\"Win32\"],\"Win32_Data_HtmlHelp\":[\"Win32_Data\"],\"Win32_Data_RightsManagement\":[\"Win32_Data\"],\"Win32_Data_Xml\":[\"Win32_Data\"],\"Win32_Data_Xml_MsXml\":[\"Win32_Data_Xml\"],\"Win32_Data_Xml_XmlLite\":[\"Win32_Data_Xml\"],\"Win32_Devices\":[\"Win32\"],\"Win32_Devices_AllJoyn\":[\"Win32_Devices\"],\"Win32_Devices_BiometricFramework\":[\"Win32_Devices\"],\"Win32_Devices_Bluetooth\":[\"Win32_Devices\"],\"Win32_Devices_Communication\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAccess\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAndDriverInstallation\":[\"Win32_Devices\"],\"Win32_Devices_DeviceQuery\":[\"Win32_Devices\"],\"Win32_Devices_Display\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration_Pnp\":[\"Win32_Devices_Enumeration\"],\"Win32_Devices_Fax\":[\"Win32_Devices\"],\"Win32_Devices_FunctionDiscovery\":[\"Win32_Devices\"],\"Win32_Devices_Geolocation\":[\"Win32_Devices\"],\"Win32_Devices_HumanInterfaceDevice\":[\"Win32_Devices\"],\"Win32_Devices_ImageAcquisition\":[\"Win32_Devices\"],\"Win32_Devices_PortableDevices\":[\"Win32_Devices\"],\"Win32_Devices_Properties\":[\"Win32_Devices\"],\"Win32_Devices_Pwm\":[\"Win32_Devices\"],\"Win32_Devices_Sensors\":[\"Win32_Devices\"],\"Win32_Devices_SerialCommunication\":[\"Win32_Devices\"],\"Win32_Devices_Tapi\":[\"Win32_Devices\"],\"Win32_Devices_Usb\":[\"Win32_Devices\"],\"Win32_Devices_WebServicesOnDevices\":[\"Win32_Devices\"],\"Win32_Foundation\":[\"Win32\"],\"Win32_Gaming\":[\"Win32\"],\"Win32_Globalization\":[\"Win32\"],\"Win32_Graphics\":[\"Win32\"],\"Win32_Graphics_Dwm\":[\"Win32_Graphics\"],\"Win32_Graphics_Gdi\":[\"Win32_Graphics\"],\"Win32_Graphics_Hlsl\":[\"Win32_Graphics\"],\"Win32_Graphics_OpenGL\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing_PrintTicket\":[\"Win32_Graphics_Printing\"],\"Win32_Management\":[\"Win32\"],\"Win32_Management_MobileDeviceManagementRegistration\":[\"Win32_Management\"],\"Win32_Media\":[\"Win32\"],\"Win32_Media_Audio\":[\"Win32_Media\"],\"Win32_Media_Audio_Apo\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_DirectMusic\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_Endpoints\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_XAudio2\":[\"Win32_Media_Audio\"],\"Win32_Media_DeviceManager\":[\"Win32_Media\"],\"Win32_Media_DxMediaObjects\":[\"Win32_Media\"],\"Win32_Media_KernelStreaming\":[\"Win32_Media\"],\"Win32_Media_LibrarySharingServices\":[\"Win32_Media\"],\"Win32_Media_MediaPlayer\":[\"Win32_Media\"],\"Win32_Media_Multimedia\":[\"Win32_Media\"],\"Win32_Media_Speech\":[\"Win32_Media\"],\"Win32_Media_Streaming\":[\"Win32_Media\"],\"Win32_Media_WindowsMediaFormat\":[\"Win32_Media\"],\"Win32_NetworkManagement\":[\"Win32\"],\"Win32_NetworkManagement_Dhcp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Dns\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_InternetConnectionWizard\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_IpHelper\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_MobileBroadband\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Multicast\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Ndis\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetBios\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetManagement\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetShell\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkDiagnosticsFramework\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkPolicyServer\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_P2P\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_QoS\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Rras\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Snmp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WNet\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WebDav\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WiFi\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectNow\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectionManager\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFilteringPlatform\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFirewall\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsNetworkVirtualization\":[\"Win32_NetworkManagement\"],\"Win32_Networking\":[\"Win32\"],\"Win32_Networking_ActiveDirectory\":[\"Win32_Networking\"],\"Win32_Networking_BackgroundIntelligentTransferService\":[\"Win32_Networking\"],\"Win32_Networking_Clustering\":[\"Win32_Networking\"],\"Win32_Networking_HttpServer\":[\"Win32_Networking\"],\"Win32_Networking_Ldap\":[\"Win32_Networking\"],\"Win32_Networking_NetworkListManager\":[\"Win32_Networking\"],\"Win32_Networking_RemoteDifferentialCompression\":[\"Win32_Networking\"],\"Win32_Networking_WebSocket\":[\"Win32_Networking\"],\"Win32_Networking_WinHttp\":[\"Win32_Networking\"],\"Win32_Networking_WinInet\":[\"Win32_Networking\"],\"Win32_Networking_WinSock\":[\"Win32_Networking\"],\"Win32_Networking_WindowsWebServices\":[\"Win32_Networking\"],\"Win32_Security\":[\"Win32\"],\"Win32_Security_AppLocker\":[\"Win32_Security\"],\"Win32_Security_Authentication\":[\"Win32_Security\"],\"Win32_Security_Authentication_Identity\":[\"Win32_Security_Authentication\"],\"Win32_Security_Authentication_Identity_Provider\":[\"Win32_Security_Authentication_Identity\"],\"Win32_Security_Authorization\":[\"Win32_Security\"],\"Win32_Security_Authorization_UI\":[\"Win32_Security_Authorization\"],\"Win32_Security_ConfigurationSnapin\":[\"Win32_Security\"],\"Win32_Security_Credentials\":[\"Win32_Security\"],\"Win32_Security_Cryptography\":[\"Win32_Security\"],\"Win32_Security_Cryptography_Catalog\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Certificates\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Sip\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_UI\":[\"Win32_Security_Cryptography\"],\"Win32_Security_DiagnosticDataQuery\":[\"Win32_Security\"],\"Win32_Security_DirectoryServices\":[\"Win32_Security\"],\"Win32_Security_EnterpriseData\":[\"Win32_Security\"],\"Win32_Security_ExtensibleAuthenticationProtocol\":[\"Win32_Security\"],\"Win32_Security_Isolation\":[\"Win32_Security\"],\"Win32_Security_LicenseProtection\":[\"Win32_Security\"],\"Win32_Security_NetworkAccessProtection\":[\"Win32_Security\"],\"Win32_Security_Tpm\":[\"Win32_Security\"],\"Win32_Security_WinTrust\":[\"Win32_Security\"],\"Win32_Security_WinWlx\":[\"Win32_Security\"],\"Win32_Storage\":[\"Win32\"],\"Win32_Storage_Cabinets\":[\"Win32_Storage\"],\"Win32_Storage_CloudFilters\":[\"Win32_Storage\"],\"Win32_Storage_Compression\":[\"Win32_Storage\"],\"Win32_Storage_DataDeduplication\":[\"Win32_Storage\"],\"Win32_Storage_DistributedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_EnhancedStorage\":[\"Win32_Storage\"],\"Win32_Storage_FileHistory\":[\"Win32_Storage\"],\"Win32_Storage_FileServerResourceManager\":[\"Win32_Storage\"],\"Win32_Storage_FileSystem\":[\"Win32_Storage\"],\"Win32_Storage_Imapi\":[\"Win32_Storage\"],\"Win32_Storage_IndexServer\":[\"Win32_Storage\"],\"Win32_Storage_InstallableFileSystems\":[\"Win32_Storage\"],\"Win32_Storage_IscsiDisc\":[\"Win32_Storage\"],\"Win32_Storage_Jet\":[\"Win32_Storage\"],\"Win32_Storage_OfflineFiles\":[\"Win32_Storage\"],\"Win32_Storage_OperationRecorder\":[\"Win32_Storage\"],\"Win32_Storage_Packaging\":[\"Win32_Storage\"],\"Win32_Storage_Packaging_Appx\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_Packaging_Opc\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_ProjectedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_StructuredStorage\":[\"Win32_Storage\"],\"Win32_Storage_Vhd\":[\"Win32_Storage\"],\"Win32_Storage_VirtualDiskService\":[\"Win32_Storage\"],\"Win32_Storage_Vss\":[\"Win32_Storage\"],\"Win32_Storage_Xps\":[\"Win32_Storage\"],\"Win32_Storage_Xps_Printing\":[\"Win32_Storage_Xps\"],\"Win32_System\":[\"Win32\"],\"Win32_System_AddressBook\":[\"Win32_System\"],\"Win32_System_Antimalware\":[\"Win32_System\"],\"Win32_System_ApplicationInstallationAndServicing\":[\"Win32_System\"],\"Win32_System_ApplicationVerifier\":[\"Win32_System\"],\"Win32_System_AssessmentTool\":[\"Win32_System\"],\"Win32_System_ClrHosting\":[\"Win32_System\"],\"Win32_System_Com\":[\"Win32_System\"],\"Win32_System_Com_CallObj\":[\"Win32_System_Com\"],\"Win32_System_Com_ChannelCredentials\":[\"Win32_System_Com\"],\"Win32_System_Com_Events\":[\"Win32_System_Com\"],\"Win32_System_Com_Marshal\":[\"Win32_System_Com\"],\"Win32_System_Com_StructuredStorage\":[\"Win32_System_Com\"],\"Win32_System_Com_UI\":[\"Win32_System_Com\"],\"Win32_System_Com_Urlmon\":[\"Win32_System_Com\"],\"Win32_System_ComponentServices\":[\"Win32_System\"],\"Win32_System_Console\":[\"Win32_System\"],\"Win32_System_Contacts\":[\"Win32_System\"],\"Win32_System_CorrelationVector\":[\"Win32_System\"],\"Win32_System_DataExchange\":[\"Win32_System\"],\"Win32_System_DeploymentServices\":[\"Win32_System\"],\"Win32_System_DesktopSharing\":[\"Win32_System\"],\"Win32_System_DeveloperLicensing\":[\"Win32_System\"],\"Win32_System_Diagnostics\":[\"Win32_System\"],\"Win32_System_Diagnostics_Ceip\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ClrProfiling\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug_ActiveScript\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Debug_Extensions\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Etw\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ProcessSnapshotting\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ToolHelp\":[\"Win32_System_Diagnostics\"],\"Win32_System_DistributedTransactionCoordinator\":[\"Win32_System\"],\"Win32_System_Environment\":[\"Win32_System\"],\"Win32_System_ErrorReporting\":[\"Win32_System\"],\"Win32_System_EventCollector\":[\"Win32_System\"],\"Win32_System_EventLog\":[\"Win32_System\"],\"Win32_System_EventNotificationService\":[\"Win32_System\"],\"Win32_System_GroupPolicy\":[\"Win32_System\"],\"Win32_System_HostCompute\":[\"Win32_System\"],\"Win32_System_HostComputeNetwork\":[\"Win32_System\"],\"Win32_System_HostComputeSystem\":[\"Win32_System\"],\"Win32_System_Hypervisor\":[\"Win32_System\"],\"Win32_System_IO\":[\"Win32_System\"],\"Win32_System_Iis\":[\"Win32_System\"],\"Win32_System_Ioctl\":[\"Win32_System\"],\"Win32_System_JobObjects\":[\"Win32_System\"],\"Win32_System_Js\":[\"Win32_System\"],\"Win32_System_Kernel\":[\"Win32_System\"],\"Win32_System_LibraryLoader\":[\"Win32_System\"],\"Win32_System_Mailslots\":[\"Win32_System\"],\"Win32_System_Mapi\":[\"Win32_System\"],\"Win32_System_Memory\":[\"Win32_System\"],\"Win32_System_Memory_NonVolatile\":[\"Win32_System_Memory\"],\"Win32_System_MessageQueuing\":[\"Win32_System\"],\"Win32_System_MixedReality\":[\"Win32_System\"],\"Win32_System_Mmc\":[\"Win32_System\"],\"Win32_System_Ole\":[\"Win32_System\"],\"Win32_System_ParentalControls\":[\"Win32_System\"],\"Win32_System_PasswordManagement\":[\"Win32_System\"],\"Win32_System_Performance\":[\"Win32_System\"],\"Win32_System_Performance_HardwareCounterProfiling\":[\"Win32_System_Performance\"],\"Win32_System_Pipes\":[\"Win32_System\"],\"Win32_System_Power\":[\"Win32_System\"],\"Win32_System_ProcessStatus\":[\"Win32_System\"],\"Win32_System_RealTimeCommunications\":[\"Win32_System\"],\"Win32_System_Recovery\":[\"Win32_System\"],\"Win32_System_Registry\":[\"Win32_System\"],\"Win32_System_RemoteAssistance\":[\"Win32_System\"],\"Win32_System_RemoteDesktop\":[\"Win32_System\"],\"Win32_System_RemoteManagement\":[\"Win32_System\"],\"Win32_System_RestartManager\":[\"Win32_System\"],\"Win32_System_Restore\":[\"Win32_System\"],\"Win32_System_Rpc\":[\"Win32_System\"],\"Win32_System_Search\":[\"Win32_System\"],\"Win32_System_Search_Common\":[\"Win32_System_Search\"],\"Win32_System_SecurityCenter\":[\"Win32_System\"],\"Win32_System_ServerBackup\":[\"Win32_System\"],\"Win32_System_Services\":[\"Win32_System\"],\"Win32_System_SettingsManagementInfrastructure\":[\"Win32_System\"],\"Win32_System_SetupAndMigration\":[\"Win32_System\"],\"Win32_System_Shutdown\":[\"Win32_System\"],\"Win32_System_StationsAndDesktops\":[\"Win32_System\"],\"Win32_System_SubsystemForLinux\":[\"Win32_System\"],\"Win32_System_SystemInformation\":[\"Win32_System\"],\"Win32_System_SystemServices\":[\"Win32_System\"],\"Win32_System_TaskScheduler\":[\"Win32_System\"],\"Win32_System_Threading\":[\"Win32_System\"],\"Win32_System_Time\":[\"Win32_System\"],\"Win32_System_TpmBaseServices\":[\"Win32_System\"],\"Win32_System_UpdateAgent\":[\"Win32_System\"],\"Win32_System_UpdateAssessment\":[\"Win32_System\"],\"Win32_System_UserAccessLogging\":[\"Win32_System\"],\"Win32_System_VirtualDosMachines\":[\"Win32_System\"],\"Win32_System_WindowsProgramming\":[\"Win32_System\"],\"Win32_System_WindowsSync\":[\"Win32_System\"],\"Win32_System_Wmi\":[\"Win32_System\"],\"Win32_UI\":[\"Win32\"],\"Win32_UI_Accessibility\":[\"Win32_UI\"],\"Win32_UI_Animation\":[\"Win32_UI\"],\"Win32_UI_ColorSystem\":[\"Win32_UI\"],\"Win32_UI_Controls\":[\"Win32_UI\"],\"Win32_UI_Controls_Dialogs\":[\"Win32_UI_Controls\"],\"Win32_UI_Controls_RichEdit\":[\"Win32_UI_Controls\"],\"Win32_UI_HiDpi\":[\"Win32_UI\"],\"Win32_UI_Input\":[\"Win32_UI\"],\"Win32_UI_Input_Ime\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Ink\":[\"Win32_UI_Input\"],\"Win32_UI_Input_KeyboardAndMouse\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Pointer\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Radial\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Touch\":[\"Win32_UI_Input\"],\"Win32_UI_Input_XboxController\":[\"Win32_UI_Input\"],\"Win32_UI_InteractionContext\":[\"Win32_UI\"],\"Win32_UI_LegacyWindowsEnvironmentFeatures\":[\"Win32_UI\"],\"Win32_UI_Magnification\":[\"Win32_UI\"],\"Win32_UI_Notifications\":[\"Win32_UI\"],\"Win32_UI_Ribbon\":[\"Win32_UI\"],\"Win32_UI_Shell\":[\"Win32_UI\"],\"Win32_UI_Shell_Common\":[\"Win32_UI_Shell\"],\"Win32_UI_Shell_PropertiesSystem\":[\"Win32_UI_Shell\"],\"Win32_UI_TabletPC\":[\"Win32_UI\"],\"Win32_UI_TextServices\":[\"Win32_UI\"],\"Win32_UI_WindowsAndMessaging\":[\"Win32_UI\"],\"Win32_UI_Wpf\":[\"Win32_UI\"],\"Win32_Web\":[\"Win32\"],\"Win32_Web_InternetExplorer\":[\"Win32_Web\"],\"default\":[]}}", "windows-sys_0.52.0": "{\"dependencies\":[{\"name\":\"windows-targets\",\"req\":\"^0.52.0\"}],\"features\":{\"Wdk\":[],\"Wdk_Foundation\":[\"Wdk\"],\"Wdk_Graphics\":[\"Wdk\"],\"Wdk_Graphics_Direct3D\":[\"Wdk_Graphics\"],\"Wdk_Storage\":[\"Wdk\"],\"Wdk_Storage_FileSystem\":[\"Wdk_Storage\"],\"Wdk_Storage_FileSystem_Minifilters\":[\"Wdk_Storage_FileSystem\"],\"Wdk_System\":[\"Wdk\"],\"Wdk_System_IO\":[\"Wdk_System\"],\"Wdk_System_OfflineRegistry\":[\"Wdk_System\"],\"Wdk_System_Registry\":[\"Wdk_System\"],\"Wdk_System_SystemInformation\":[\"Wdk_System\"],\"Wdk_System_SystemServices\":[\"Wdk_System\"],\"Wdk_System_Threading\":[\"Wdk_System\"],\"Win32\":[],\"Win32_Data\":[\"Win32\"],\"Win32_Data_HtmlHelp\":[\"Win32_Data\"],\"Win32_Data_RightsManagement\":[\"Win32_Data\"],\"Win32_Devices\":[\"Win32\"],\"Win32_Devices_AllJoyn\":[\"Win32_Devices\"],\"Win32_Devices_BiometricFramework\":[\"Win32_Devices\"],\"Win32_Devices_Bluetooth\":[\"Win32_Devices\"],\"Win32_Devices_Communication\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAndDriverInstallation\":[\"Win32_Devices\"],\"Win32_Devices_DeviceQuery\":[\"Win32_Devices\"],\"Win32_Devices_Display\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration_Pnp\":[\"Win32_Devices_Enumeration\"],\"Win32_Devices_Fax\":[\"Win32_Devices\"],\"Win32_Devices_HumanInterfaceDevice\":[\"Win32_Devices\"],\"Win32_Devices_PortableDevices\":[\"Win32_Devices\"],\"Win32_Devices_Properties\":[\"Win32_Devices\"],\"Win32_Devices_Pwm\":[\"Win32_Devices\"],\"Win32_Devices_Sensors\":[\"Win32_Devices\"],\"Win32_Devices_SerialCommunication\":[\"Win32_Devices\"],\"Win32_Devices_Tapi\":[\"Win32_Devices\"],\"Win32_Devices_Usb\":[\"Win32_Devices\"],\"Win32_Devices_WebServicesOnDevices\":[\"Win32_Devices\"],\"Win32_Foundation\":[\"Win32\"],\"Win32_Gaming\":[\"Win32\"],\"Win32_Globalization\":[\"Win32\"],\"Win32_Graphics\":[\"Win32\"],\"Win32_Graphics_Dwm\":[\"Win32_Graphics\"],\"Win32_Graphics_Gdi\":[\"Win32_Graphics\"],\"Win32_Graphics_GdiPlus\":[\"Win32_Graphics\"],\"Win32_Graphics_Hlsl\":[\"Win32_Graphics\"],\"Win32_Graphics_OpenGL\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing_PrintTicket\":[\"Win32_Graphics_Printing\"],\"Win32_Management\":[\"Win32\"],\"Win32_Management_MobileDeviceManagementRegistration\":[\"Win32_Management\"],\"Win32_Media\":[\"Win32\"],\"Win32_Media_Audio\":[\"Win32_Media\"],\"Win32_Media_DxMediaObjects\":[\"Win32_Media\"],\"Win32_Media_KernelStreaming\":[\"Win32_Media\"],\"Win32_Media_Multimedia\":[\"Win32_Media\"],\"Win32_Media_Streaming\":[\"Win32_Media\"],\"Win32_Media_WindowsMediaFormat\":[\"Win32_Media\"],\"Win32_NetworkManagement\":[\"Win32\"],\"Win32_NetworkManagement_Dhcp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Dns\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_InternetConnectionWizard\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_IpHelper\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Multicast\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Ndis\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetBios\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetManagement\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetShell\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkDiagnosticsFramework\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_P2P\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_QoS\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Rras\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Snmp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WNet\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WebDav\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WiFi\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectionManager\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFilteringPlatform\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFirewall\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsNetworkVirtualization\":[\"Win32_NetworkManagement\"],\"Win32_Networking\":[\"Win32\"],\"Win32_Networking_ActiveDirectory\":[\"Win32_Networking\"],\"Win32_Networking_Clustering\":[\"Win32_Networking\"],\"Win32_Networking_HttpServer\":[\"Win32_Networking\"],\"Win32_Networking_Ldap\":[\"Win32_Networking\"],\"Win32_Networking_WebSocket\":[\"Win32_Networking\"],\"Win32_Networking_WinHttp\":[\"Win32_Networking\"],\"Win32_Networking_WinInet\":[\"Win32_Networking\"],\"Win32_Networking_WinSock\":[\"Win32_Networking\"],\"Win32_Networking_WindowsWebServices\":[\"Win32_Networking\"],\"Win32_Security\":[\"Win32\"],\"Win32_Security_AppLocker\":[\"Win32_Security\"],\"Win32_Security_Authentication\":[\"Win32_Security\"],\"Win32_Security_Authentication_Identity\":[\"Win32_Security_Authentication\"],\"Win32_Security_Authorization\":[\"Win32_Security\"],\"Win32_Security_Credentials\":[\"Win32_Security\"],\"Win32_Security_Cryptography\":[\"Win32_Security\"],\"Win32_Security_Cryptography_Catalog\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Certificates\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Sip\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_UI\":[\"Win32_Security_Cryptography\"],\"Win32_Security_DiagnosticDataQuery\":[\"Win32_Security\"],\"Win32_Security_DirectoryServices\":[\"Win32_Security\"],\"Win32_Security_EnterpriseData\":[\"Win32_Security\"],\"Win32_Security_ExtensibleAuthenticationProtocol\":[\"Win32_Security\"],\"Win32_Security_Isolation\":[\"Win32_Security\"],\"Win32_Security_LicenseProtection\":[\"Win32_Security\"],\"Win32_Security_NetworkAccessProtection\":[\"Win32_Security\"],\"Win32_Security_WinTrust\":[\"Win32_Security\"],\"Win32_Security_WinWlx\":[\"Win32_Security\"],\"Win32_Storage\":[\"Win32\"],\"Win32_Storage_Cabinets\":[\"Win32_Storage\"],\"Win32_Storage_CloudFilters\":[\"Win32_Storage\"],\"Win32_Storage_Compression\":[\"Win32_Storage\"],\"Win32_Storage_DistributedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_FileHistory\":[\"Win32_Storage\"],\"Win32_Storage_FileSystem\":[\"Win32_Storage\"],\"Win32_Storage_Imapi\":[\"Win32_Storage\"],\"Win32_Storage_IndexServer\":[\"Win32_Storage\"],\"Win32_Storage_InstallableFileSystems\":[\"Win32_Storage\"],\"Win32_Storage_IscsiDisc\":[\"Win32_Storage\"],\"Win32_Storage_Jet\":[\"Win32_Storage\"],\"Win32_Storage_Nvme\":[\"Win32_Storage\"],\"Win32_Storage_OfflineFiles\":[\"Win32_Storage\"],\"Win32_Storage_OperationRecorder\":[\"Win32_Storage\"],\"Win32_Storage_Packaging\":[\"Win32_Storage\"],\"Win32_Storage_Packaging_Appx\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_ProjectedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_StructuredStorage\":[\"Win32_Storage\"],\"Win32_Storage_Vhd\":[\"Win32_Storage\"],\"Win32_Storage_Xps\":[\"Win32_Storage\"],\"Win32_System\":[\"Win32\"],\"Win32_System_AddressBook\":[\"Win32_System\"],\"Win32_System_Antimalware\":[\"Win32_System\"],\"Win32_System_ApplicationInstallationAndServicing\":[\"Win32_System\"],\"Win32_System_ApplicationVerifier\":[\"Win32_System\"],\"Win32_System_ClrHosting\":[\"Win32_System\"],\"Win32_System_Com\":[\"Win32_System\"],\"Win32_System_Com_Marshal\":[\"Win32_System_Com\"],\"Win32_System_Com_StructuredStorage\":[\"Win32_System_Com\"],\"Win32_System_Com_Urlmon\":[\"Win32_System_Com\"],\"Win32_System_ComponentServices\":[\"Win32_System\"],\"Win32_System_Console\":[\"Win32_System\"],\"Win32_System_CorrelationVector\":[\"Win32_System\"],\"Win32_System_DataExchange\":[\"Win32_System\"],\"Win32_System_DeploymentServices\":[\"Win32_System\"],\"Win32_System_DeveloperLicensing\":[\"Win32_System\"],\"Win32_System_Diagnostics\":[\"Win32_System\"],\"Win32_System_Diagnostics_Ceip\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug_Extensions\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Etw\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ProcessSnapshotting\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ToolHelp\":[\"Win32_System_Diagnostics\"],\"Win32_System_DistributedTransactionCoordinator\":[\"Win32_System\"],\"Win32_System_Environment\":[\"Win32_System\"],\"Win32_System_ErrorReporting\":[\"Win32_System\"],\"Win32_System_EventCollector\":[\"Win32_System\"],\"Win32_System_EventLog\":[\"Win32_System\"],\"Win32_System_EventNotificationService\":[\"Win32_System\"],\"Win32_System_GroupPolicy\":[\"Win32_System\"],\"Win32_System_HostCompute\":[\"Win32_System\"],\"Win32_System_HostComputeNetwork\":[\"Win32_System\"],\"Win32_System_HostComputeSystem\":[\"Win32_System\"],\"Win32_System_Hypervisor\":[\"Win32_System\"],\"Win32_System_IO\":[\"Win32_System\"],\"Win32_System_Iis\":[\"Win32_System\"],\"Win32_System_Ioctl\":[\"Win32_System\"],\"Win32_System_JobObjects\":[\"Win32_System\"],\"Win32_System_Js\":[\"Win32_System\"],\"Win32_System_Kernel\":[\"Win32_System\"],\"Win32_System_LibraryLoader\":[\"Win32_System\"],\"Win32_System_Mailslots\":[\"Win32_System\"],\"Win32_System_Mapi\":[\"Win32_System\"],\"Win32_System_Memory\":[\"Win32_System\"],\"Win32_System_Memory_NonVolatile\":[\"Win32_System_Memory\"],\"Win32_System_MessageQueuing\":[\"Win32_System\"],\"Win32_System_MixedReality\":[\"Win32_System\"],\"Win32_System_Ole\":[\"Win32_System\"],\"Win32_System_PasswordManagement\":[\"Win32_System\"],\"Win32_System_Performance\":[\"Win32_System\"],\"Win32_System_Performance_HardwareCounterProfiling\":[\"Win32_System_Performance\"],\"Win32_System_Pipes\":[\"Win32_System\"],\"Win32_System_Power\":[\"Win32_System\"],\"Win32_System_ProcessStatus\":[\"Win32_System\"],\"Win32_System_Recovery\":[\"Win32_System\"],\"Win32_System_Registry\":[\"Win32_System\"],\"Win32_System_RemoteDesktop\":[\"Win32_System\"],\"Win32_System_RemoteManagement\":[\"Win32_System\"],\"Win32_System_RestartManager\":[\"Win32_System\"],\"Win32_System_Restore\":[\"Win32_System\"],\"Win32_System_Rpc\":[\"Win32_System\"],\"Win32_System_Search\":[\"Win32_System\"],\"Win32_System_Search_Common\":[\"Win32_System_Search\"],\"Win32_System_SecurityCenter\":[\"Win32_System\"],\"Win32_System_Services\":[\"Win32_System\"],\"Win32_System_SetupAndMigration\":[\"Win32_System\"],\"Win32_System_Shutdown\":[\"Win32_System\"],\"Win32_System_StationsAndDesktops\":[\"Win32_System\"],\"Win32_System_SubsystemForLinux\":[\"Win32_System\"],\"Win32_System_SystemInformation\":[\"Win32_System\"],\"Win32_System_SystemServices\":[\"Win32_System\"],\"Win32_System_Threading\":[\"Win32_System\"],\"Win32_System_Time\":[\"Win32_System\"],\"Win32_System_TpmBaseServices\":[\"Win32_System\"],\"Win32_System_UserAccessLogging\":[\"Win32_System\"],\"Win32_System_Variant\":[\"Win32_System\"],\"Win32_System_VirtualDosMachines\":[\"Win32_System\"],\"Win32_System_WindowsProgramming\":[\"Win32_System\"],\"Win32_System_Wmi\":[\"Win32_System\"],\"Win32_UI\":[\"Win32\"],\"Win32_UI_Accessibility\":[\"Win32_UI\"],\"Win32_UI_ColorSystem\":[\"Win32_UI\"],\"Win32_UI_Controls\":[\"Win32_UI\"],\"Win32_UI_Controls_Dialogs\":[\"Win32_UI_Controls\"],\"Win32_UI_HiDpi\":[\"Win32_UI\"],\"Win32_UI_Input\":[\"Win32_UI\"],\"Win32_UI_Input_Ime\":[\"Win32_UI_Input\"],\"Win32_UI_Input_KeyboardAndMouse\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Pointer\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Touch\":[\"Win32_UI_Input\"],\"Win32_UI_Input_XboxController\":[\"Win32_UI_Input\"],\"Win32_UI_InteractionContext\":[\"Win32_UI\"],\"Win32_UI_Magnification\":[\"Win32_UI\"],\"Win32_UI_Shell\":[\"Win32_UI\"],\"Win32_UI_Shell_PropertiesSystem\":[\"Win32_UI_Shell\"],\"Win32_UI_TabletPC\":[\"Win32_UI\"],\"Win32_UI_TextServices\":[\"Win32_UI\"],\"Win32_UI_WindowsAndMessaging\":[\"Win32_UI\"],\"Win32_Web\":[\"Win32\"],\"Win32_Web_InternetExplorer\":[\"Win32_Web\"],\"default\":[],\"docs\":[]}}", "windows-sys_0.59.0": "{\"dependencies\":[{\"name\":\"windows-targets\",\"req\":\"^0.52.6\"}],\"features\":{\"Wdk\":[\"Win32_Foundation\"],\"Wdk_Devices\":[\"Wdk\"],\"Wdk_Devices_Bluetooth\":[\"Wdk_Devices\"],\"Wdk_Devices_HumanInterfaceDevice\":[\"Wdk_Devices\"],\"Wdk_Foundation\":[\"Wdk\"],\"Wdk_Graphics\":[\"Wdk\"],\"Wdk_Graphics_Direct3D\":[\"Wdk_Graphics\"],\"Wdk_NetworkManagement\":[\"Wdk\"],\"Wdk_NetworkManagement_Ndis\":[\"Wdk_NetworkManagement\"],\"Wdk_NetworkManagement_WindowsFilteringPlatform\":[\"Wdk_NetworkManagement\"],\"Wdk_Storage\":[\"Wdk\"],\"Wdk_Storage_FileSystem\":[\"Wdk_Storage\"],\"Wdk_Storage_FileSystem_Minifilters\":[\"Wdk_Storage_FileSystem\"],\"Wdk_System\":[\"Wdk\"],\"Wdk_System_IO\":[\"Wdk_System\"],\"Wdk_System_Memory\":[\"Wdk_System\"],\"Wdk_System_OfflineRegistry\":[\"Wdk_System\"],\"Wdk_System_Registry\":[\"Wdk_System\"],\"Wdk_System_SystemInformation\":[\"Wdk_System\"],\"Wdk_System_SystemServices\":[\"Wdk_System\"],\"Wdk_System_Threading\":[\"Wdk_System\"],\"Win32\":[\"Win32_Foundation\"],\"Win32_Data\":[\"Win32\"],\"Win32_Data_HtmlHelp\":[\"Win32_Data\"],\"Win32_Data_RightsManagement\":[\"Win32_Data\"],\"Win32_Devices\":[\"Win32\"],\"Win32_Devices_AllJoyn\":[\"Win32_Devices\"],\"Win32_Devices_BiometricFramework\":[\"Win32_Devices\"],\"Win32_Devices_Bluetooth\":[\"Win32_Devices\"],\"Win32_Devices_Communication\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAndDriverInstallation\":[\"Win32_Devices\"],\"Win32_Devices_DeviceQuery\":[\"Win32_Devices\"],\"Win32_Devices_Display\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration_Pnp\":[\"Win32_Devices_Enumeration\"],\"Win32_Devices_Fax\":[\"Win32_Devices\"],\"Win32_Devices_HumanInterfaceDevice\":[\"Win32_Devices\"],\"Win32_Devices_PortableDevices\":[\"Win32_Devices\"],\"Win32_Devices_Properties\":[\"Win32_Devices\"],\"Win32_Devices_Pwm\":[\"Win32_Devices\"],\"Win32_Devices_Sensors\":[\"Win32_Devices\"],\"Win32_Devices_SerialCommunication\":[\"Win32_Devices\"],\"Win32_Devices_Tapi\":[\"Win32_Devices\"],\"Win32_Devices_Usb\":[\"Win32_Devices\"],\"Win32_Devices_WebServicesOnDevices\":[\"Win32_Devices\"],\"Win32_Foundation\":[\"Win32\"],\"Win32_Gaming\":[\"Win32\"],\"Win32_Globalization\":[\"Win32\"],\"Win32_Graphics\":[\"Win32\"],\"Win32_Graphics_Dwm\":[\"Win32_Graphics\"],\"Win32_Graphics_Gdi\":[\"Win32_Graphics\"],\"Win32_Graphics_GdiPlus\":[\"Win32_Graphics\"],\"Win32_Graphics_Hlsl\":[\"Win32_Graphics\"],\"Win32_Graphics_OpenGL\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing_PrintTicket\":[\"Win32_Graphics_Printing\"],\"Win32_Management\":[\"Win32\"],\"Win32_Management_MobileDeviceManagementRegistration\":[\"Win32_Management\"],\"Win32_Media\":[\"Win32\"],\"Win32_Media_Audio\":[\"Win32_Media\"],\"Win32_Media_DxMediaObjects\":[\"Win32_Media\"],\"Win32_Media_KernelStreaming\":[\"Win32_Media\"],\"Win32_Media_Multimedia\":[\"Win32_Media\"],\"Win32_Media_Streaming\":[\"Win32_Media\"],\"Win32_Media_WindowsMediaFormat\":[\"Win32_Media\"],\"Win32_NetworkManagement\":[\"Win32\"],\"Win32_NetworkManagement_Dhcp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Dns\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_InternetConnectionWizard\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_IpHelper\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Multicast\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Ndis\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetBios\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetManagement\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetShell\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkDiagnosticsFramework\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_P2P\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_QoS\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Rras\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Snmp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WNet\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WebDav\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WiFi\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectionManager\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFilteringPlatform\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFirewall\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsNetworkVirtualization\":[\"Win32_NetworkManagement\"],\"Win32_Networking\":[\"Win32\"],\"Win32_Networking_ActiveDirectory\":[\"Win32_Networking\"],\"Win32_Networking_Clustering\":[\"Win32_Networking\"],\"Win32_Networking_HttpServer\":[\"Win32_Networking\"],\"Win32_Networking_Ldap\":[\"Win32_Networking\"],\"Win32_Networking_WebSocket\":[\"Win32_Networking\"],\"Win32_Networking_WinHttp\":[\"Win32_Networking\"],\"Win32_Networking_WinInet\":[\"Win32_Networking\"],\"Win32_Networking_WinSock\":[\"Win32_Networking\"],\"Win32_Networking_WindowsWebServices\":[\"Win32_Networking\"],\"Win32_Security\":[\"Win32\"],\"Win32_Security_AppLocker\":[\"Win32_Security\"],\"Win32_Security_Authentication\":[\"Win32_Security\"],\"Win32_Security_Authentication_Identity\":[\"Win32_Security_Authentication\"],\"Win32_Security_Authorization\":[\"Win32_Security\"],\"Win32_Security_Credentials\":[\"Win32_Security\"],\"Win32_Security_Cryptography\":[\"Win32_Security\"],\"Win32_Security_Cryptography_Catalog\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Certificates\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Sip\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_UI\":[\"Win32_Security_Cryptography\"],\"Win32_Security_DiagnosticDataQuery\":[\"Win32_Security\"],\"Win32_Security_DirectoryServices\":[\"Win32_Security\"],\"Win32_Security_EnterpriseData\":[\"Win32_Security\"],\"Win32_Security_ExtensibleAuthenticationProtocol\":[\"Win32_Security\"],\"Win32_Security_Isolation\":[\"Win32_Security\"],\"Win32_Security_LicenseProtection\":[\"Win32_Security\"],\"Win32_Security_NetworkAccessProtection\":[\"Win32_Security\"],\"Win32_Security_WinTrust\":[\"Win32_Security\"],\"Win32_Security_WinWlx\":[\"Win32_Security\"],\"Win32_Storage\":[\"Win32\"],\"Win32_Storage_Cabinets\":[\"Win32_Storage\"],\"Win32_Storage_CloudFilters\":[\"Win32_Storage\"],\"Win32_Storage_Compression\":[\"Win32_Storage\"],\"Win32_Storage_DistributedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_FileHistory\":[\"Win32_Storage\"],\"Win32_Storage_FileSystem\":[\"Win32_Storage\"],\"Win32_Storage_Imapi\":[\"Win32_Storage\"],\"Win32_Storage_IndexServer\":[\"Win32_Storage\"],\"Win32_Storage_InstallableFileSystems\":[\"Win32_Storage\"],\"Win32_Storage_IscsiDisc\":[\"Win32_Storage\"],\"Win32_Storage_Jet\":[\"Win32_Storage\"],\"Win32_Storage_Nvme\":[\"Win32_Storage\"],\"Win32_Storage_OfflineFiles\":[\"Win32_Storage\"],\"Win32_Storage_OperationRecorder\":[\"Win32_Storage\"],\"Win32_Storage_Packaging\":[\"Win32_Storage\"],\"Win32_Storage_Packaging_Appx\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_ProjectedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_StructuredStorage\":[\"Win32_Storage\"],\"Win32_Storage_Vhd\":[\"Win32_Storage\"],\"Win32_Storage_Xps\":[\"Win32_Storage\"],\"Win32_System\":[\"Win32\"],\"Win32_System_AddressBook\":[\"Win32_System\"],\"Win32_System_Antimalware\":[\"Win32_System\"],\"Win32_System_ApplicationInstallationAndServicing\":[\"Win32_System\"],\"Win32_System_ApplicationVerifier\":[\"Win32_System\"],\"Win32_System_ClrHosting\":[\"Win32_System\"],\"Win32_System_Com\":[\"Win32_System\"],\"Win32_System_Com_Marshal\":[\"Win32_System_Com\"],\"Win32_System_Com_StructuredStorage\":[\"Win32_System_Com\"],\"Win32_System_Com_Urlmon\":[\"Win32_System_Com\"],\"Win32_System_ComponentServices\":[\"Win32_System\"],\"Win32_System_Console\":[\"Win32_System\"],\"Win32_System_CorrelationVector\":[\"Win32_System\"],\"Win32_System_DataExchange\":[\"Win32_System\"],\"Win32_System_DeploymentServices\":[\"Win32_System\"],\"Win32_System_DeveloperLicensing\":[\"Win32_System\"],\"Win32_System_Diagnostics\":[\"Win32_System\"],\"Win32_System_Diagnostics_Ceip\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug_Extensions\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Etw\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ProcessSnapshotting\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ToolHelp\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_TraceLogging\":[\"Win32_System_Diagnostics\"],\"Win32_System_DistributedTransactionCoordinator\":[\"Win32_System\"],\"Win32_System_Environment\":[\"Win32_System\"],\"Win32_System_ErrorReporting\":[\"Win32_System\"],\"Win32_System_EventCollector\":[\"Win32_System\"],\"Win32_System_EventLog\":[\"Win32_System\"],\"Win32_System_EventNotificationService\":[\"Win32_System\"],\"Win32_System_GroupPolicy\":[\"Win32_System\"],\"Win32_System_HostCompute\":[\"Win32_System\"],\"Win32_System_HostComputeNetwork\":[\"Win32_System\"],\"Win32_System_HostComputeSystem\":[\"Win32_System\"],\"Win32_System_Hypervisor\":[\"Win32_System\"],\"Win32_System_IO\":[\"Win32_System\"],\"Win32_System_Iis\":[\"Win32_System\"],\"Win32_System_Ioctl\":[\"Win32_System\"],\"Win32_System_JobObjects\":[\"Win32_System\"],\"Win32_System_Js\":[\"Win32_System\"],\"Win32_System_Kernel\":[\"Win32_System\"],\"Win32_System_LibraryLoader\":[\"Win32_System\"],\"Win32_System_Mailslots\":[\"Win32_System\"],\"Win32_System_Mapi\":[\"Win32_System\"],\"Win32_System_Memory\":[\"Win32_System\"],\"Win32_System_Memory_NonVolatile\":[\"Win32_System_Memory\"],\"Win32_System_MessageQueuing\":[\"Win32_System\"],\"Win32_System_MixedReality\":[\"Win32_System\"],\"Win32_System_Ole\":[\"Win32_System\"],\"Win32_System_PasswordManagement\":[\"Win32_System\"],\"Win32_System_Performance\":[\"Win32_System\"],\"Win32_System_Performance_HardwareCounterProfiling\":[\"Win32_System_Performance\"],\"Win32_System_Pipes\":[\"Win32_System\"],\"Win32_System_Power\":[\"Win32_System\"],\"Win32_System_ProcessStatus\":[\"Win32_System\"],\"Win32_System_Recovery\":[\"Win32_System\"],\"Win32_System_Registry\":[\"Win32_System\"],\"Win32_System_RemoteDesktop\":[\"Win32_System\"],\"Win32_System_RemoteManagement\":[\"Win32_System\"],\"Win32_System_RestartManager\":[\"Win32_System\"],\"Win32_System_Restore\":[\"Win32_System\"],\"Win32_System_Rpc\":[\"Win32_System\"],\"Win32_System_Search\":[\"Win32_System\"],\"Win32_System_Search_Common\":[\"Win32_System_Search\"],\"Win32_System_SecurityCenter\":[\"Win32_System\"],\"Win32_System_Services\":[\"Win32_System\"],\"Win32_System_SetupAndMigration\":[\"Win32_System\"],\"Win32_System_Shutdown\":[\"Win32_System\"],\"Win32_System_StationsAndDesktops\":[\"Win32_System\"],\"Win32_System_SubsystemForLinux\":[\"Win32_System\"],\"Win32_System_SystemInformation\":[\"Win32_System\"],\"Win32_System_SystemServices\":[\"Win32_System\"],\"Win32_System_Threading\":[\"Win32_System\"],\"Win32_System_Time\":[\"Win32_System\"],\"Win32_System_TpmBaseServices\":[\"Win32_System\"],\"Win32_System_UserAccessLogging\":[\"Win32_System\"],\"Win32_System_Variant\":[\"Win32_System\"],\"Win32_System_VirtualDosMachines\":[\"Win32_System\"],\"Win32_System_WindowsProgramming\":[\"Win32_System\"],\"Win32_System_Wmi\":[\"Win32_System\"],\"Win32_UI\":[\"Win32\"],\"Win32_UI_Accessibility\":[\"Win32_UI\"],\"Win32_UI_ColorSystem\":[\"Win32_UI\"],\"Win32_UI_Controls\":[\"Win32_UI\"],\"Win32_UI_Controls_Dialogs\":[\"Win32_UI_Controls\"],\"Win32_UI_HiDpi\":[\"Win32_UI\"],\"Win32_UI_Input\":[\"Win32_UI\"],\"Win32_UI_Input_Ime\":[\"Win32_UI_Input\"],\"Win32_UI_Input_KeyboardAndMouse\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Pointer\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Touch\":[\"Win32_UI_Input\"],\"Win32_UI_Input_XboxController\":[\"Win32_UI_Input\"],\"Win32_UI_InteractionContext\":[\"Win32_UI\"],\"Win32_UI_Magnification\":[\"Win32_UI\"],\"Win32_UI_Shell\":[\"Win32_UI\"],\"Win32_UI_Shell_Common\":[\"Win32_UI_Shell\"],\"Win32_UI_Shell_PropertiesSystem\":[\"Win32_UI_Shell\"],\"Win32_UI_TabletPC\":[\"Win32_UI\"],\"Win32_UI_TextServices\":[\"Win32_UI\"],\"Win32_UI_WindowsAndMessaging\":[\"Win32_UI\"],\"Win32_Web\":[\"Win32\"],\"Win32_Web_InternetExplorer\":[\"Win32_Web\"],\"default\":[],\"docs\":[]}}", "windows-sys_0.60.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-targets\",\"req\":\"^0.53.2\"}],\"features\":{\"Wdk\":[\"Win32_Foundation\"],\"Wdk_Devices\":[\"Wdk\"],\"Wdk_Devices_Bluetooth\":[\"Wdk_Devices\"],\"Wdk_Devices_HumanInterfaceDevice\":[\"Wdk_Devices\"],\"Wdk_Foundation\":[\"Wdk\"],\"Wdk_Graphics\":[\"Wdk\"],\"Wdk_Graphics_Direct3D\":[\"Wdk_Graphics\"],\"Wdk_NetworkManagement\":[\"Wdk\"],\"Wdk_NetworkManagement_Ndis\":[\"Wdk_NetworkManagement\"],\"Wdk_NetworkManagement_WindowsFilteringPlatform\":[\"Wdk_NetworkManagement\"],\"Wdk_Storage\":[\"Wdk\"],\"Wdk_Storage_FileSystem\":[\"Wdk_Storage\"],\"Wdk_Storage_FileSystem_Minifilters\":[\"Wdk_Storage_FileSystem\"],\"Wdk_System\":[\"Wdk\"],\"Wdk_System_IO\":[\"Wdk_System\"],\"Wdk_System_Memory\":[\"Wdk_System\"],\"Wdk_System_OfflineRegistry\":[\"Wdk_System\"],\"Wdk_System_Registry\":[\"Wdk_System\"],\"Wdk_System_SystemInformation\":[\"Wdk_System\"],\"Wdk_System_SystemServices\":[\"Wdk_System\"],\"Wdk_System_Threading\":[\"Wdk_System\"],\"Win32\":[\"Win32_Foundation\"],\"Win32_Data\":[\"Win32\"],\"Win32_Data_HtmlHelp\":[\"Win32_Data\"],\"Win32_Data_RightsManagement\":[\"Win32_Data\"],\"Win32_Devices\":[\"Win32\"],\"Win32_Devices_AllJoyn\":[\"Win32_Devices\"],\"Win32_Devices_Beep\":[\"Win32_Devices\"],\"Win32_Devices_BiometricFramework\":[\"Win32_Devices\"],\"Win32_Devices_Bluetooth\":[\"Win32_Devices\"],\"Win32_Devices_Cdrom\":[\"Win32_Devices\"],\"Win32_Devices_Communication\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAndDriverInstallation\":[\"Win32_Devices\"],\"Win32_Devices_DeviceQuery\":[\"Win32_Devices\"],\"Win32_Devices_Display\":[\"Win32_Devices\"],\"Win32_Devices_Dvd\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration_Pnp\":[\"Win32_Devices_Enumeration\"],\"Win32_Devices_Fax\":[\"Win32_Devices\"],\"Win32_Devices_HumanInterfaceDevice\":[\"Win32_Devices\"],\"Win32_Devices_Nfc\":[\"Win32_Devices\"],\"Win32_Devices_Nfp\":[\"Win32_Devices\"],\"Win32_Devices_PortableDevices\":[\"Win32_Devices\"],\"Win32_Devices_Properties\":[\"Win32_Devices\"],\"Win32_Devices_Pwm\":[\"Win32_Devices\"],\"Win32_Devices_Sensors\":[\"Win32_Devices\"],\"Win32_Devices_SerialCommunication\":[\"Win32_Devices\"],\"Win32_Devices_Tapi\":[\"Win32_Devices\"],\"Win32_Devices_Usb\":[\"Win32_Devices\"],\"Win32_Devices_WebServicesOnDevices\":[\"Win32_Devices\"],\"Win32_Foundation\":[\"Win32\"],\"Win32_Gaming\":[\"Win32\"],\"Win32_Globalization\":[\"Win32\"],\"Win32_Graphics\":[\"Win32\"],\"Win32_Graphics_Dwm\":[\"Win32_Graphics\"],\"Win32_Graphics_Gdi\":[\"Win32_Graphics\"],\"Win32_Graphics_GdiPlus\":[\"Win32_Graphics\"],\"Win32_Graphics_Hlsl\":[\"Win32_Graphics\"],\"Win32_Graphics_OpenGL\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing_PrintTicket\":[\"Win32_Graphics_Printing\"],\"Win32_Management\":[\"Win32\"],\"Win32_Management_MobileDeviceManagementRegistration\":[\"Win32_Management\"],\"Win32_Media\":[\"Win32\"],\"Win32_Media_Audio\":[\"Win32_Media\"],\"Win32_Media_DxMediaObjects\":[\"Win32_Media\"],\"Win32_Media_KernelStreaming\":[\"Win32_Media\"],\"Win32_Media_Multimedia\":[\"Win32_Media\"],\"Win32_Media_Streaming\":[\"Win32_Media\"],\"Win32_Media_WindowsMediaFormat\":[\"Win32_Media\"],\"Win32_NetworkManagement\":[\"Win32\"],\"Win32_NetworkManagement_Dhcp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Dns\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_InternetConnectionWizard\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_IpHelper\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Multicast\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Ndis\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetBios\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetManagement\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetShell\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkDiagnosticsFramework\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_P2P\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_QoS\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Rras\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Snmp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WNet\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WebDav\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WiFi\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectionManager\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFilteringPlatform\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFirewall\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsNetworkVirtualization\":[\"Win32_NetworkManagement\"],\"Win32_Networking\":[\"Win32\"],\"Win32_Networking_ActiveDirectory\":[\"Win32_Networking\"],\"Win32_Networking_Clustering\":[\"Win32_Networking\"],\"Win32_Networking_HttpServer\":[\"Win32_Networking\"],\"Win32_Networking_Ldap\":[\"Win32_Networking\"],\"Win32_Networking_WebSocket\":[\"Win32_Networking\"],\"Win32_Networking_WinHttp\":[\"Win32_Networking\"],\"Win32_Networking_WinInet\":[\"Win32_Networking\"],\"Win32_Networking_WinSock\":[\"Win32_Networking\"],\"Win32_Networking_WindowsWebServices\":[\"Win32_Networking\"],\"Win32_Security\":[\"Win32\"],\"Win32_Security_AppLocker\":[\"Win32_Security\"],\"Win32_Security_Authentication\":[\"Win32_Security\"],\"Win32_Security_Authentication_Identity\":[\"Win32_Security_Authentication\"],\"Win32_Security_Authorization\":[\"Win32_Security\"],\"Win32_Security_Credentials\":[\"Win32_Security\"],\"Win32_Security_Cryptography\":[\"Win32_Security\"],\"Win32_Security_Cryptography_Catalog\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Certificates\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Sip\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_UI\":[\"Win32_Security_Cryptography\"],\"Win32_Security_DiagnosticDataQuery\":[\"Win32_Security\"],\"Win32_Security_DirectoryServices\":[\"Win32_Security\"],\"Win32_Security_EnterpriseData\":[\"Win32_Security\"],\"Win32_Security_ExtensibleAuthenticationProtocol\":[\"Win32_Security\"],\"Win32_Security_Isolation\":[\"Win32_Security\"],\"Win32_Security_LicenseProtection\":[\"Win32_Security\"],\"Win32_Security_NetworkAccessProtection\":[\"Win32_Security\"],\"Win32_Security_WinTrust\":[\"Win32_Security\"],\"Win32_Security_WinWlx\":[\"Win32_Security\"],\"Win32_Storage\":[\"Win32\"],\"Win32_Storage_Cabinets\":[\"Win32_Storage\"],\"Win32_Storage_CloudFilters\":[\"Win32_Storage\"],\"Win32_Storage_Compression\":[\"Win32_Storage\"],\"Win32_Storage_DistributedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_FileHistory\":[\"Win32_Storage\"],\"Win32_Storage_FileSystem\":[\"Win32_Storage\"],\"Win32_Storage_Imapi\":[\"Win32_Storage\"],\"Win32_Storage_IndexServer\":[\"Win32_Storage\"],\"Win32_Storage_InstallableFileSystems\":[\"Win32_Storage\"],\"Win32_Storage_IscsiDisc\":[\"Win32_Storage\"],\"Win32_Storage_Jet\":[\"Win32_Storage\"],\"Win32_Storage_Nvme\":[\"Win32_Storage\"],\"Win32_Storage_OfflineFiles\":[\"Win32_Storage\"],\"Win32_Storage_OperationRecorder\":[\"Win32_Storage\"],\"Win32_Storage_Packaging\":[\"Win32_Storage\"],\"Win32_Storage_Packaging_Appx\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_ProjectedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_StructuredStorage\":[\"Win32_Storage\"],\"Win32_Storage_Vhd\":[\"Win32_Storage\"],\"Win32_Storage_Xps\":[\"Win32_Storage\"],\"Win32_System\":[\"Win32\"],\"Win32_System_AddressBook\":[\"Win32_System\"],\"Win32_System_Antimalware\":[\"Win32_System\"],\"Win32_System_ApplicationInstallationAndServicing\":[\"Win32_System\"],\"Win32_System_ApplicationVerifier\":[\"Win32_System\"],\"Win32_System_ClrHosting\":[\"Win32_System\"],\"Win32_System_Com\":[\"Win32_System\"],\"Win32_System_Com_Marshal\":[\"Win32_System_Com\"],\"Win32_System_Com_StructuredStorage\":[\"Win32_System_Com\"],\"Win32_System_Com_Urlmon\":[\"Win32_System_Com\"],\"Win32_System_ComponentServices\":[\"Win32_System\"],\"Win32_System_Console\":[\"Win32_System\"],\"Win32_System_CorrelationVector\":[\"Win32_System\"],\"Win32_System_DataExchange\":[\"Win32_System\"],\"Win32_System_DeploymentServices\":[\"Win32_System\"],\"Win32_System_DeveloperLicensing\":[\"Win32_System\"],\"Win32_System_Diagnostics\":[\"Win32_System\"],\"Win32_System_Diagnostics_Ceip\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug_Extensions\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Etw\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ProcessSnapshotting\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ToolHelp\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_TraceLogging\":[\"Win32_System_Diagnostics\"],\"Win32_System_DistributedTransactionCoordinator\":[\"Win32_System\"],\"Win32_System_Environment\":[\"Win32_System\"],\"Win32_System_ErrorReporting\":[\"Win32_System\"],\"Win32_System_EventCollector\":[\"Win32_System\"],\"Win32_System_EventLog\":[\"Win32_System\"],\"Win32_System_EventNotificationService\":[\"Win32_System\"],\"Win32_System_GroupPolicy\":[\"Win32_System\"],\"Win32_System_HostCompute\":[\"Win32_System\"],\"Win32_System_HostComputeNetwork\":[\"Win32_System\"],\"Win32_System_HostComputeSystem\":[\"Win32_System\"],\"Win32_System_Hypervisor\":[\"Win32_System\"],\"Win32_System_IO\":[\"Win32_System\"],\"Win32_System_Iis\":[\"Win32_System\"],\"Win32_System_Ioctl\":[\"Win32_System\"],\"Win32_System_JobObjects\":[\"Win32_System\"],\"Win32_System_Js\":[\"Win32_System\"],\"Win32_System_Kernel\":[\"Win32_System\"],\"Win32_System_LibraryLoader\":[\"Win32_System\"],\"Win32_System_Mailslots\":[\"Win32_System\"],\"Win32_System_Mapi\":[\"Win32_System\"],\"Win32_System_Memory\":[\"Win32_System\"],\"Win32_System_Memory_NonVolatile\":[\"Win32_System_Memory\"],\"Win32_System_MessageQueuing\":[\"Win32_System\"],\"Win32_System_MixedReality\":[\"Win32_System\"],\"Win32_System_Ole\":[\"Win32_System\"],\"Win32_System_PasswordManagement\":[\"Win32_System\"],\"Win32_System_Performance\":[\"Win32_System\"],\"Win32_System_Performance_HardwareCounterProfiling\":[\"Win32_System_Performance\"],\"Win32_System_Pipes\":[\"Win32_System\"],\"Win32_System_Power\":[\"Win32_System\"],\"Win32_System_ProcessStatus\":[\"Win32_System\"],\"Win32_System_Recovery\":[\"Win32_System\"],\"Win32_System_Registry\":[\"Win32_System\"],\"Win32_System_RemoteDesktop\":[\"Win32_System\"],\"Win32_System_RemoteManagement\":[\"Win32_System\"],\"Win32_System_RestartManager\":[\"Win32_System\"],\"Win32_System_Restore\":[\"Win32_System\"],\"Win32_System_Rpc\":[\"Win32_System\"],\"Win32_System_Search\":[\"Win32_System\"],\"Win32_System_Search_Common\":[\"Win32_System_Search\"],\"Win32_System_SecurityCenter\":[\"Win32_System\"],\"Win32_System_Services\":[\"Win32_System\"],\"Win32_System_SetupAndMigration\":[\"Win32_System\"],\"Win32_System_Shutdown\":[\"Win32_System\"],\"Win32_System_StationsAndDesktops\":[\"Win32_System\"],\"Win32_System_SubsystemForLinux\":[\"Win32_System\"],\"Win32_System_SystemInformation\":[\"Win32_System\"],\"Win32_System_SystemServices\":[\"Win32_System\"],\"Win32_System_Threading\":[\"Win32_System\"],\"Win32_System_Time\":[\"Win32_System\"],\"Win32_System_TpmBaseServices\":[\"Win32_System\"],\"Win32_System_UserAccessLogging\":[\"Win32_System\"],\"Win32_System_Variant\":[\"Win32_System\"],\"Win32_System_VirtualDosMachines\":[\"Win32_System\"],\"Win32_System_WindowsProgramming\":[\"Win32_System\"],\"Win32_System_Wmi\":[\"Win32_System\"],\"Win32_UI\":[\"Win32\"],\"Win32_UI_Accessibility\":[\"Win32_UI\"],\"Win32_UI_ColorSystem\":[\"Win32_UI\"],\"Win32_UI_Controls\":[\"Win32_UI\"],\"Win32_UI_Controls_Dialogs\":[\"Win32_UI_Controls\"],\"Win32_UI_HiDpi\":[\"Win32_UI\"],\"Win32_UI_Input\":[\"Win32_UI\"],\"Win32_UI_Input_Ime\":[\"Win32_UI_Input\"],\"Win32_UI_Input_KeyboardAndMouse\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Pointer\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Touch\":[\"Win32_UI_Input\"],\"Win32_UI_Input_XboxController\":[\"Win32_UI_Input\"],\"Win32_UI_InteractionContext\":[\"Win32_UI\"],\"Win32_UI_Magnification\":[\"Win32_UI\"],\"Win32_UI_Shell\":[\"Win32_UI\"],\"Win32_UI_Shell_Common\":[\"Win32_UI_Shell\"],\"Win32_UI_Shell_PropertiesSystem\":[\"Win32_UI_Shell\"],\"Win32_UI_TabletPC\":[\"Win32_UI\"],\"Win32_UI_TextServices\":[\"Win32_UI\"],\"Win32_UI_WindowsAndMessaging\":[\"Win32_UI\"],\"Win32_Web\":[\"Win32\"],\"Win32_Web_InternetExplorer\":[\"Win32_Web\"],\"default\":[],\"docs\":[]}}", - "windows-sys_0.61.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.2.0\"}],\"features\":{\"Wdk\":[\"Win32_Foundation\"],\"Wdk_Devices\":[\"Wdk\"],\"Wdk_Devices_Bluetooth\":[\"Wdk_Devices\"],\"Wdk_Devices_HumanInterfaceDevice\":[\"Wdk_Devices\"],\"Wdk_Foundation\":[\"Wdk\"],\"Wdk_Graphics\":[\"Wdk\"],\"Wdk_Graphics_Direct3D\":[\"Wdk_Graphics\"],\"Wdk_NetworkManagement\":[\"Wdk\"],\"Wdk_NetworkManagement_Ndis\":[\"Wdk_NetworkManagement\"],\"Wdk_NetworkManagement_WindowsFilteringPlatform\":[\"Wdk_NetworkManagement\"],\"Wdk_Storage\":[\"Wdk\"],\"Wdk_Storage_FileSystem\":[\"Wdk_Storage\"],\"Wdk_Storage_FileSystem_Minifilters\":[\"Wdk_Storage_FileSystem\"],\"Wdk_System\":[\"Wdk\"],\"Wdk_System_IO\":[\"Wdk_System\"],\"Wdk_System_Memory\":[\"Wdk_System\"],\"Wdk_System_OfflineRegistry\":[\"Wdk_System\"],\"Wdk_System_Registry\":[\"Wdk_System\"],\"Wdk_System_SystemInformation\":[\"Wdk_System\"],\"Wdk_System_SystemServices\":[\"Wdk_System\"],\"Wdk_System_Threading\":[\"Wdk_System\"],\"Win32\":[\"Win32_Foundation\"],\"Win32_Data\":[\"Win32\"],\"Win32_Data_HtmlHelp\":[\"Win32_Data\"],\"Win32_Data_RightsManagement\":[\"Win32_Data\"],\"Win32_Devices\":[\"Win32\"],\"Win32_Devices_AllJoyn\":[\"Win32_Devices\"],\"Win32_Devices_Beep\":[\"Win32_Devices\"],\"Win32_Devices_BiometricFramework\":[\"Win32_Devices\"],\"Win32_Devices_Bluetooth\":[\"Win32_Devices\"],\"Win32_Devices_Cdrom\":[\"Win32_Devices\"],\"Win32_Devices_Communication\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAndDriverInstallation\":[\"Win32_Devices\"],\"Win32_Devices_DeviceQuery\":[\"Win32_Devices\"],\"Win32_Devices_Display\":[\"Win32_Devices\"],\"Win32_Devices_Dvd\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration_Pnp\":[\"Win32_Devices_Enumeration\"],\"Win32_Devices_Fax\":[\"Win32_Devices\"],\"Win32_Devices_HumanInterfaceDevice\":[\"Win32_Devices\"],\"Win32_Devices_Nfc\":[\"Win32_Devices\"],\"Win32_Devices_Nfp\":[\"Win32_Devices\"],\"Win32_Devices_PortableDevices\":[\"Win32_Devices\"],\"Win32_Devices_Properties\":[\"Win32_Devices\"],\"Win32_Devices_Pwm\":[\"Win32_Devices\"],\"Win32_Devices_Sensors\":[\"Win32_Devices\"],\"Win32_Devices_SerialCommunication\":[\"Win32_Devices\"],\"Win32_Devices_Tapi\":[\"Win32_Devices\"],\"Win32_Devices_Usb\":[\"Win32_Devices\"],\"Win32_Devices_WebServicesOnDevices\":[\"Win32_Devices\"],\"Win32_Foundation\":[\"Win32\"],\"Win32_Gaming\":[\"Win32\"],\"Win32_Globalization\":[\"Win32\"],\"Win32_Graphics\":[\"Win32\"],\"Win32_Graphics_Dwm\":[\"Win32_Graphics\"],\"Win32_Graphics_Gdi\":[\"Win32_Graphics\"],\"Win32_Graphics_GdiPlus\":[\"Win32_Graphics\"],\"Win32_Graphics_Hlsl\":[\"Win32_Graphics\"],\"Win32_Graphics_OpenGL\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing_PrintTicket\":[\"Win32_Graphics_Printing\"],\"Win32_Management\":[\"Win32\"],\"Win32_Management_MobileDeviceManagementRegistration\":[\"Win32_Management\"],\"Win32_Media\":[\"Win32\"],\"Win32_Media_Audio\":[\"Win32_Media\"],\"Win32_Media_DxMediaObjects\":[\"Win32_Media\"],\"Win32_Media_KernelStreaming\":[\"Win32_Media\"],\"Win32_Media_Multimedia\":[\"Win32_Media\"],\"Win32_Media_Streaming\":[\"Win32_Media\"],\"Win32_Media_WindowsMediaFormat\":[\"Win32_Media\"],\"Win32_NetworkManagement\":[\"Win32\"],\"Win32_NetworkManagement_Dhcp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Dns\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_InternetConnectionWizard\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_IpHelper\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Multicast\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Ndis\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetBios\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetManagement\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetShell\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkDiagnosticsFramework\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_P2P\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_QoS\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Rras\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Snmp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WNet\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WebDav\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WiFi\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectionManager\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFilteringPlatform\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFirewall\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsNetworkVirtualization\":[\"Win32_NetworkManagement\"],\"Win32_Networking\":[\"Win32\"],\"Win32_Networking_ActiveDirectory\":[\"Win32_Networking\"],\"Win32_Networking_Clustering\":[\"Win32_Networking\"],\"Win32_Networking_HttpServer\":[\"Win32_Networking\"],\"Win32_Networking_Ldap\":[\"Win32_Networking\"],\"Win32_Networking_WebSocket\":[\"Win32_Networking\"],\"Win32_Networking_WinHttp\":[\"Win32_Networking\"],\"Win32_Networking_WinInet\":[\"Win32_Networking\"],\"Win32_Networking_WinSock\":[\"Win32_Networking\"],\"Win32_Networking_WindowsWebServices\":[\"Win32_Networking\"],\"Win32_Security\":[\"Win32\"],\"Win32_Security_AppLocker\":[\"Win32_Security\"],\"Win32_Security_Authentication\":[\"Win32_Security\"],\"Win32_Security_Authentication_Identity\":[\"Win32_Security_Authentication\"],\"Win32_Security_Authorization\":[\"Win32_Security\"],\"Win32_Security_Credentials\":[\"Win32_Security\"],\"Win32_Security_Cryptography\":[\"Win32_Security\"],\"Win32_Security_Cryptography_Catalog\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Certificates\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Sip\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_UI\":[\"Win32_Security_Cryptography\"],\"Win32_Security_DiagnosticDataQuery\":[\"Win32_Security\"],\"Win32_Security_DirectoryServices\":[\"Win32_Security\"],\"Win32_Security_EnterpriseData\":[\"Win32_Security\"],\"Win32_Security_ExtensibleAuthenticationProtocol\":[\"Win32_Security\"],\"Win32_Security_Isolation\":[\"Win32_Security\"],\"Win32_Security_LicenseProtection\":[\"Win32_Security\"],\"Win32_Security_NetworkAccessProtection\":[\"Win32_Security\"],\"Win32_Security_WinTrust\":[\"Win32_Security\"],\"Win32_Security_WinWlx\":[\"Win32_Security\"],\"Win32_Storage\":[\"Win32\"],\"Win32_Storage_Cabinets\":[\"Win32_Storage\"],\"Win32_Storage_CloudFilters\":[\"Win32_Storage\"],\"Win32_Storage_Compression\":[\"Win32_Storage\"],\"Win32_Storage_DistributedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_FileHistory\":[\"Win32_Storage\"],\"Win32_Storage_FileSystem\":[\"Win32_Storage\"],\"Win32_Storage_Imapi\":[\"Win32_Storage\"],\"Win32_Storage_IndexServer\":[\"Win32_Storage\"],\"Win32_Storage_InstallableFileSystems\":[\"Win32_Storage\"],\"Win32_Storage_IscsiDisc\":[\"Win32_Storage\"],\"Win32_Storage_Jet\":[\"Win32_Storage\"],\"Win32_Storage_Nvme\":[\"Win32_Storage\"],\"Win32_Storage_OfflineFiles\":[\"Win32_Storage\"],\"Win32_Storage_OperationRecorder\":[\"Win32_Storage\"],\"Win32_Storage_Packaging\":[\"Win32_Storage\"],\"Win32_Storage_Packaging_Appx\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_ProjectedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_StructuredStorage\":[\"Win32_Storage\"],\"Win32_Storage_Vhd\":[\"Win32_Storage\"],\"Win32_Storage_Xps\":[\"Win32_Storage\"],\"Win32_System\":[\"Win32\"],\"Win32_System_AddressBook\":[\"Win32_System\"],\"Win32_System_Antimalware\":[\"Win32_System\"],\"Win32_System_ApplicationInstallationAndServicing\":[\"Win32_System\"],\"Win32_System_ApplicationVerifier\":[\"Win32_System\"],\"Win32_System_ClrHosting\":[\"Win32_System\"],\"Win32_System_Com\":[\"Win32_System\"],\"Win32_System_Com_Marshal\":[\"Win32_System_Com\"],\"Win32_System_Com_StructuredStorage\":[\"Win32_System_Com\"],\"Win32_System_Com_Urlmon\":[\"Win32_System_Com\"],\"Win32_System_ComponentServices\":[\"Win32_System\"],\"Win32_System_Console\":[\"Win32_System\"],\"Win32_System_CorrelationVector\":[\"Win32_System\"],\"Win32_System_DataExchange\":[\"Win32_System\"],\"Win32_System_DeploymentServices\":[\"Win32_System\"],\"Win32_System_DeveloperLicensing\":[\"Win32_System\"],\"Win32_System_Diagnostics\":[\"Win32_System\"],\"Win32_System_Diagnostics_Ceip\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug_Extensions\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Etw\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ProcessSnapshotting\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ToolHelp\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_TraceLogging\":[\"Win32_System_Diagnostics\"],\"Win32_System_DistributedTransactionCoordinator\":[\"Win32_System\"],\"Win32_System_Environment\":[\"Win32_System\"],\"Win32_System_ErrorReporting\":[\"Win32_System\"],\"Win32_System_EventCollector\":[\"Win32_System\"],\"Win32_System_EventLog\":[\"Win32_System\"],\"Win32_System_EventNotificationService\":[\"Win32_System\"],\"Win32_System_GroupPolicy\":[\"Win32_System\"],\"Win32_System_HostCompute\":[\"Win32_System\"],\"Win32_System_HostComputeNetwork\":[\"Win32_System\"],\"Win32_System_HostComputeSystem\":[\"Win32_System\"],\"Win32_System_Hypervisor\":[\"Win32_System\"],\"Win32_System_IO\":[\"Win32_System\"],\"Win32_System_Iis\":[\"Win32_System\"],\"Win32_System_Ioctl\":[\"Win32_System\"],\"Win32_System_JobObjects\":[\"Win32_System\"],\"Win32_System_Js\":[\"Win32_System\"],\"Win32_System_Kernel\":[\"Win32_System\"],\"Win32_System_LibraryLoader\":[\"Win32_System\"],\"Win32_System_Mailslots\":[\"Win32_System\"],\"Win32_System_Mapi\":[\"Win32_System\"],\"Win32_System_Memory\":[\"Win32_System\"],\"Win32_System_Memory_NonVolatile\":[\"Win32_System_Memory\"],\"Win32_System_MessageQueuing\":[\"Win32_System\"],\"Win32_System_MixedReality\":[\"Win32_System\"],\"Win32_System_Ole\":[\"Win32_System\"],\"Win32_System_PasswordManagement\":[\"Win32_System\"],\"Win32_System_Performance\":[\"Win32_System\"],\"Win32_System_Performance_HardwareCounterProfiling\":[\"Win32_System_Performance\"],\"Win32_System_Pipes\":[\"Win32_System\"],\"Win32_System_Power\":[\"Win32_System\"],\"Win32_System_ProcessStatus\":[\"Win32_System\"],\"Win32_System_Recovery\":[\"Win32_System\"],\"Win32_System_Registry\":[\"Win32_System\"],\"Win32_System_RemoteDesktop\":[\"Win32_System\"],\"Win32_System_RemoteManagement\":[\"Win32_System\"],\"Win32_System_RestartManager\":[\"Win32_System\"],\"Win32_System_Restore\":[\"Win32_System\"],\"Win32_System_Rpc\":[\"Win32_System\"],\"Win32_System_Search\":[\"Win32_System\"],\"Win32_System_Search_Common\":[\"Win32_System_Search\"],\"Win32_System_SecurityCenter\":[\"Win32_System\"],\"Win32_System_Services\":[\"Win32_System\"],\"Win32_System_SetupAndMigration\":[\"Win32_System\"],\"Win32_System_Shutdown\":[\"Win32_System\"],\"Win32_System_StationsAndDesktops\":[\"Win32_System\"],\"Win32_System_SubsystemForLinux\":[\"Win32_System\"],\"Win32_System_SystemInformation\":[\"Win32_System\"],\"Win32_System_SystemServices\":[\"Win32_System\"],\"Win32_System_Threading\":[\"Win32_System\"],\"Win32_System_Time\":[\"Win32_System\"],\"Win32_System_TpmBaseServices\":[\"Win32_System\"],\"Win32_System_UserAccessLogging\":[\"Win32_System\"],\"Win32_System_Variant\":[\"Win32_System\"],\"Win32_System_VirtualDosMachines\":[\"Win32_System\"],\"Win32_System_WindowsProgramming\":[\"Win32_System\"],\"Win32_System_Wmi\":[\"Win32_System\"],\"Win32_UI\":[\"Win32\"],\"Win32_UI_Accessibility\":[\"Win32_UI\"],\"Win32_UI_ColorSystem\":[\"Win32_UI\"],\"Win32_UI_Controls\":[\"Win32_UI\"],\"Win32_UI_Controls_Dialogs\":[\"Win32_UI_Controls\"],\"Win32_UI_HiDpi\":[\"Win32_UI\"],\"Win32_UI_Input\":[\"Win32_UI\"],\"Win32_UI_Input_Ime\":[\"Win32_UI_Input\"],\"Win32_UI_Input_KeyboardAndMouse\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Pointer\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Touch\":[\"Win32_UI_Input\"],\"Win32_UI_Input_XboxController\":[\"Win32_UI_Input\"],\"Win32_UI_InteractionContext\":[\"Win32_UI\"],\"Win32_UI_Magnification\":[\"Win32_UI\"],\"Win32_UI_Shell\":[\"Win32_UI\"],\"Win32_UI_Shell_Common\":[\"Win32_UI_Shell\"],\"Win32_UI_Shell_PropertiesSystem\":[\"Win32_UI_Shell\"],\"Win32_UI_TabletPC\":[\"Win32_UI\"],\"Win32_UI_TextServices\":[\"Win32_UI\"],\"Win32_UI_WindowsAndMessaging\":[\"Win32_UI\"],\"Win32_Web\":[\"Win32\"],\"Win32_Web_InternetExplorer\":[\"Win32_Web\"],\"default\":[],\"docs\":[]}}", + "windows-sys_0.61.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.2.1\"}],\"features\":{\"Wdk\":[\"Win32_Foundation\"],\"Wdk_Devices\":[\"Wdk\"],\"Wdk_Devices_Bluetooth\":[\"Wdk_Devices\"],\"Wdk_Devices_HumanInterfaceDevice\":[\"Wdk_Devices\"],\"Wdk_Foundation\":[\"Wdk\"],\"Wdk_Graphics\":[\"Wdk\"],\"Wdk_Graphics_Direct3D\":[\"Wdk_Graphics\"],\"Wdk_NetworkManagement\":[\"Wdk\"],\"Wdk_NetworkManagement_Ndis\":[\"Wdk_NetworkManagement\"],\"Wdk_NetworkManagement_WindowsFilteringPlatform\":[\"Wdk_NetworkManagement\"],\"Wdk_Storage\":[\"Wdk\"],\"Wdk_Storage_FileSystem\":[\"Wdk_Storage\"],\"Wdk_Storage_FileSystem_Minifilters\":[\"Wdk_Storage_FileSystem\"],\"Wdk_System\":[\"Wdk\"],\"Wdk_System_IO\":[\"Wdk_System\"],\"Wdk_System_Memory\":[\"Wdk_System\"],\"Wdk_System_OfflineRegistry\":[\"Wdk_System\"],\"Wdk_System_Registry\":[\"Wdk_System\"],\"Wdk_System_SystemInformation\":[\"Wdk_System\"],\"Wdk_System_SystemServices\":[\"Wdk_System\"],\"Wdk_System_Threading\":[\"Wdk_System\"],\"Win32\":[\"Win32_Foundation\"],\"Win32_Data\":[\"Win32\"],\"Win32_Data_HtmlHelp\":[\"Win32_Data\"],\"Win32_Data_RightsManagement\":[\"Win32_Data\"],\"Win32_Devices\":[\"Win32\"],\"Win32_Devices_AllJoyn\":[\"Win32_Devices\"],\"Win32_Devices_Beep\":[\"Win32_Devices\"],\"Win32_Devices_BiometricFramework\":[\"Win32_Devices\"],\"Win32_Devices_Bluetooth\":[\"Win32_Devices\"],\"Win32_Devices_Cdrom\":[\"Win32_Devices\"],\"Win32_Devices_Communication\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAndDriverInstallation\":[\"Win32_Devices\"],\"Win32_Devices_DeviceQuery\":[\"Win32_Devices\"],\"Win32_Devices_Display\":[\"Win32_Devices\"],\"Win32_Devices_Dvd\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration_Pnp\":[\"Win32_Devices_Enumeration\"],\"Win32_Devices_Fax\":[\"Win32_Devices\"],\"Win32_Devices_HumanInterfaceDevice\":[\"Win32_Devices\"],\"Win32_Devices_Nfc\":[\"Win32_Devices\"],\"Win32_Devices_Nfp\":[\"Win32_Devices\"],\"Win32_Devices_PortableDevices\":[\"Win32_Devices\"],\"Win32_Devices_Properties\":[\"Win32_Devices\"],\"Win32_Devices_Pwm\":[\"Win32_Devices\"],\"Win32_Devices_Sensors\":[\"Win32_Devices\"],\"Win32_Devices_SerialCommunication\":[\"Win32_Devices\"],\"Win32_Devices_Tapi\":[\"Win32_Devices\"],\"Win32_Devices_Usb\":[\"Win32_Devices\"],\"Win32_Devices_WebServicesOnDevices\":[\"Win32_Devices\"],\"Win32_Foundation\":[\"Win32\"],\"Win32_Gaming\":[\"Win32\"],\"Win32_Globalization\":[\"Win32\"],\"Win32_Graphics\":[\"Win32\"],\"Win32_Graphics_Dwm\":[\"Win32_Graphics\"],\"Win32_Graphics_Gdi\":[\"Win32_Graphics\"],\"Win32_Graphics_GdiPlus\":[\"Win32_Graphics\"],\"Win32_Graphics_Hlsl\":[\"Win32_Graphics\"],\"Win32_Graphics_OpenGL\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing_PrintTicket\":[\"Win32_Graphics_Printing\"],\"Win32_Management\":[\"Win32\"],\"Win32_Management_MobileDeviceManagementRegistration\":[\"Win32_Management\"],\"Win32_Media\":[\"Win32\"],\"Win32_Media_Audio\":[\"Win32_Media\"],\"Win32_Media_DxMediaObjects\":[\"Win32_Media\"],\"Win32_Media_KernelStreaming\":[\"Win32_Media\"],\"Win32_Media_Multimedia\":[\"Win32_Media\"],\"Win32_Media_Streaming\":[\"Win32_Media\"],\"Win32_Media_WindowsMediaFormat\":[\"Win32_Media\"],\"Win32_NetworkManagement\":[\"Win32\"],\"Win32_NetworkManagement_Dhcp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Dns\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_InternetConnectionWizard\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_IpHelper\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Multicast\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Ndis\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetBios\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetManagement\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetShell\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkDiagnosticsFramework\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_P2P\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_QoS\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Rras\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Snmp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WNet\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WebDav\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WiFi\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectionManager\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFilteringPlatform\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFirewall\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsNetworkVirtualization\":[\"Win32_NetworkManagement\"],\"Win32_Networking\":[\"Win32\"],\"Win32_Networking_ActiveDirectory\":[\"Win32_Networking\"],\"Win32_Networking_Clustering\":[\"Win32_Networking\"],\"Win32_Networking_HttpServer\":[\"Win32_Networking\"],\"Win32_Networking_Ldap\":[\"Win32_Networking\"],\"Win32_Networking_WebSocket\":[\"Win32_Networking\"],\"Win32_Networking_WinHttp\":[\"Win32_Networking\"],\"Win32_Networking_WinInet\":[\"Win32_Networking\"],\"Win32_Networking_WinSock\":[\"Win32_Networking\"],\"Win32_Networking_WindowsWebServices\":[\"Win32_Networking\"],\"Win32_Security\":[\"Win32\"],\"Win32_Security_AppLocker\":[\"Win32_Security\"],\"Win32_Security_Authentication\":[\"Win32_Security\"],\"Win32_Security_Authentication_Identity\":[\"Win32_Security_Authentication\"],\"Win32_Security_Authorization\":[\"Win32_Security\"],\"Win32_Security_Credentials\":[\"Win32_Security\"],\"Win32_Security_Cryptography\":[\"Win32_Security\"],\"Win32_Security_Cryptography_Catalog\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Certificates\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Sip\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_UI\":[\"Win32_Security_Cryptography\"],\"Win32_Security_DiagnosticDataQuery\":[\"Win32_Security\"],\"Win32_Security_DirectoryServices\":[\"Win32_Security\"],\"Win32_Security_EnterpriseData\":[\"Win32_Security\"],\"Win32_Security_ExtensibleAuthenticationProtocol\":[\"Win32_Security\"],\"Win32_Security_Isolation\":[\"Win32_Security\"],\"Win32_Security_LicenseProtection\":[\"Win32_Security\"],\"Win32_Security_NetworkAccessProtection\":[\"Win32_Security\"],\"Win32_Security_WinTrust\":[\"Win32_Security\"],\"Win32_Security_WinWlx\":[\"Win32_Security\"],\"Win32_Storage\":[\"Win32\"],\"Win32_Storage_Cabinets\":[\"Win32_Storage\"],\"Win32_Storage_CloudFilters\":[\"Win32_Storage\"],\"Win32_Storage_Compression\":[\"Win32_Storage\"],\"Win32_Storage_DistributedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_FileHistory\":[\"Win32_Storage\"],\"Win32_Storage_FileSystem\":[\"Win32_Storage\"],\"Win32_Storage_Imapi\":[\"Win32_Storage\"],\"Win32_Storage_IndexServer\":[\"Win32_Storage\"],\"Win32_Storage_InstallableFileSystems\":[\"Win32_Storage\"],\"Win32_Storage_IscsiDisc\":[\"Win32_Storage\"],\"Win32_Storage_Jet\":[\"Win32_Storage\"],\"Win32_Storage_Nvme\":[\"Win32_Storage\"],\"Win32_Storage_OfflineFiles\":[\"Win32_Storage\"],\"Win32_Storage_OperationRecorder\":[\"Win32_Storage\"],\"Win32_Storage_Packaging\":[\"Win32_Storage\"],\"Win32_Storage_Packaging_Appx\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_ProjectedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_StructuredStorage\":[\"Win32_Storage\"],\"Win32_Storage_Vhd\":[\"Win32_Storage\"],\"Win32_Storage_Xps\":[\"Win32_Storage\"],\"Win32_System\":[\"Win32\"],\"Win32_System_AddressBook\":[\"Win32_System\"],\"Win32_System_Antimalware\":[\"Win32_System\"],\"Win32_System_ApplicationInstallationAndServicing\":[\"Win32_System\"],\"Win32_System_ApplicationVerifier\":[\"Win32_System\"],\"Win32_System_ClrHosting\":[\"Win32_System\"],\"Win32_System_Com\":[\"Win32_System\"],\"Win32_System_Com_Marshal\":[\"Win32_System_Com\"],\"Win32_System_Com_StructuredStorage\":[\"Win32_System_Com\"],\"Win32_System_Com_Urlmon\":[\"Win32_System_Com\"],\"Win32_System_ComponentServices\":[\"Win32_System\"],\"Win32_System_Console\":[\"Win32_System\"],\"Win32_System_CorrelationVector\":[\"Win32_System\"],\"Win32_System_DataExchange\":[\"Win32_System\"],\"Win32_System_DeploymentServices\":[\"Win32_System\"],\"Win32_System_DeveloperLicensing\":[\"Win32_System\"],\"Win32_System_Diagnostics\":[\"Win32_System\"],\"Win32_System_Diagnostics_Ceip\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug_Extensions\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Etw\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ProcessSnapshotting\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ToolHelp\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_TraceLogging\":[\"Win32_System_Diagnostics\"],\"Win32_System_DistributedTransactionCoordinator\":[\"Win32_System\"],\"Win32_System_Environment\":[\"Win32_System\"],\"Win32_System_ErrorReporting\":[\"Win32_System\"],\"Win32_System_EventCollector\":[\"Win32_System\"],\"Win32_System_EventLog\":[\"Win32_System\"],\"Win32_System_EventNotificationService\":[\"Win32_System\"],\"Win32_System_GroupPolicy\":[\"Win32_System\"],\"Win32_System_HostCompute\":[\"Win32_System\"],\"Win32_System_HostComputeNetwork\":[\"Win32_System\"],\"Win32_System_HostComputeSystem\":[\"Win32_System\"],\"Win32_System_Hypervisor\":[\"Win32_System\"],\"Win32_System_IO\":[\"Win32_System\"],\"Win32_System_Iis\":[\"Win32_System\"],\"Win32_System_Ioctl\":[\"Win32_System\"],\"Win32_System_JobObjects\":[\"Win32_System\"],\"Win32_System_Js\":[\"Win32_System\"],\"Win32_System_Kernel\":[\"Win32_System\"],\"Win32_System_LibraryLoader\":[\"Win32_System\"],\"Win32_System_Mailslots\":[\"Win32_System\"],\"Win32_System_Mapi\":[\"Win32_System\"],\"Win32_System_Memory\":[\"Win32_System\"],\"Win32_System_Memory_NonVolatile\":[\"Win32_System_Memory\"],\"Win32_System_MessageQueuing\":[\"Win32_System\"],\"Win32_System_MixedReality\":[\"Win32_System\"],\"Win32_System_Ole\":[\"Win32_System\"],\"Win32_System_PasswordManagement\":[\"Win32_System\"],\"Win32_System_Performance\":[\"Win32_System\"],\"Win32_System_Performance_HardwareCounterProfiling\":[\"Win32_System_Performance\"],\"Win32_System_Pipes\":[\"Win32_System\"],\"Win32_System_Power\":[\"Win32_System\"],\"Win32_System_ProcessStatus\":[\"Win32_System\"],\"Win32_System_Recovery\":[\"Win32_System\"],\"Win32_System_Registry\":[\"Win32_System\"],\"Win32_System_RemoteDesktop\":[\"Win32_System\"],\"Win32_System_RemoteManagement\":[\"Win32_System\"],\"Win32_System_RestartManager\":[\"Win32_System\"],\"Win32_System_Restore\":[\"Win32_System\"],\"Win32_System_Rpc\":[\"Win32_System\"],\"Win32_System_Search\":[\"Win32_System\"],\"Win32_System_Search_Common\":[\"Win32_System_Search\"],\"Win32_System_SecurityCenter\":[\"Win32_System\"],\"Win32_System_Services\":[\"Win32_System\"],\"Win32_System_SetupAndMigration\":[\"Win32_System\"],\"Win32_System_Shutdown\":[\"Win32_System\"],\"Win32_System_StationsAndDesktops\":[\"Win32_System\"],\"Win32_System_SubsystemForLinux\":[\"Win32_System\"],\"Win32_System_SystemInformation\":[\"Win32_System\"],\"Win32_System_SystemServices\":[\"Win32_System\"],\"Win32_System_Threading\":[\"Win32_System\"],\"Win32_System_Time\":[\"Win32_System\"],\"Win32_System_TpmBaseServices\":[\"Win32_System\"],\"Win32_System_UserAccessLogging\":[\"Win32_System\"],\"Win32_System_Variant\":[\"Win32_System\"],\"Win32_System_VirtualDosMachines\":[\"Win32_System\"],\"Win32_System_WindowsProgramming\":[\"Win32_System\"],\"Win32_System_Wmi\":[\"Win32_System\"],\"Win32_UI\":[\"Win32\"],\"Win32_UI_Accessibility\":[\"Win32_UI\"],\"Win32_UI_ColorSystem\":[\"Win32_UI\"],\"Win32_UI_Controls\":[\"Win32_UI\"],\"Win32_UI_Controls_Dialogs\":[\"Win32_UI_Controls\"],\"Win32_UI_HiDpi\":[\"Win32_UI\"],\"Win32_UI_Input\":[\"Win32_UI\"],\"Win32_UI_Input_Ime\":[\"Win32_UI_Input\"],\"Win32_UI_Input_KeyboardAndMouse\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Pointer\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Touch\":[\"Win32_UI_Input\"],\"Win32_UI_Input_XboxController\":[\"Win32_UI_Input\"],\"Win32_UI_InteractionContext\":[\"Win32_UI\"],\"Win32_UI_Magnification\":[\"Win32_UI\"],\"Win32_UI_Shell\":[\"Win32_UI\"],\"Win32_UI_Shell_Common\":[\"Win32_UI_Shell\"],\"Win32_UI_Shell_PropertiesSystem\":[\"Win32_UI_Shell\"],\"Win32_UI_TabletPC\":[\"Win32_UI\"],\"Win32_UI_TextServices\":[\"Win32_UI\"],\"Win32_UI_WindowsAndMessaging\":[\"Win32_UI\"],\"Win32_Web\":[\"Win32\"],\"Win32_Web_InternetExplorer\":[\"Win32_Web\"],\"default\":[],\"docs\":[]}}", "windows-targets_0.42.2": "{\"dependencies\":[{\"name\":\"windows_aarch64_gnullvm\",\"req\":\"^0.42.2\",\"target\":\"aarch64-pc-windows-gnullvm\"},{\"name\":\"windows_aarch64_msvc\",\"req\":\"^0.42.2\",\"target\":\"aarch64-pc-windows-msvc\"},{\"name\":\"windows_aarch64_msvc\",\"req\":\"^0.42.2\",\"target\":\"aarch64-uwp-windows-msvc\"},{\"name\":\"windows_i686_gnu\",\"req\":\"^0.42.2\",\"target\":\"i686-pc-windows-gnu\"},{\"name\":\"windows_i686_gnu\",\"req\":\"^0.42.2\",\"target\":\"i686-uwp-windows-gnu\"},{\"name\":\"windows_i686_msvc\",\"req\":\"^0.42.2\",\"target\":\"i686-pc-windows-msvc\"},{\"name\":\"windows_i686_msvc\",\"req\":\"^0.42.2\",\"target\":\"i686-uwp-windows-msvc\"},{\"name\":\"windows_x86_64_gnu\",\"req\":\"^0.42.2\",\"target\":\"x86_64-pc-windows-gnu\"},{\"name\":\"windows_x86_64_gnu\",\"req\":\"^0.42.2\",\"target\":\"x86_64-uwp-windows-gnu\"},{\"name\":\"windows_x86_64_gnullvm\",\"req\":\"^0.42.2\",\"target\":\"x86_64-pc-windows-gnullvm\"},{\"name\":\"windows_x86_64_msvc\",\"req\":\"^0.42.2\",\"target\":\"x86_64-pc-windows-msvc\"},{\"name\":\"windows_x86_64_msvc\",\"req\":\"^0.42.2\",\"target\":\"x86_64-uwp-windows-msvc\"}],\"features\":{}}", "windows-targets_0.48.5": "{\"dependencies\":[{\"name\":\"windows_aarch64_gnullvm\",\"req\":\"^0.48.5\",\"target\":\"aarch64-pc-windows-gnullvm\"},{\"name\":\"windows_aarch64_msvc\",\"req\":\"^0.48.5\",\"target\":\"cfg(all(target_arch = \\\"aarch64\\\", target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"},{\"name\":\"windows_i686_gnu\",\"req\":\"^0.48.5\",\"target\":\"cfg(all(target_arch = \\\"x86\\\", target_env = \\\"gnu\\\", not(windows_raw_dylib)))\"},{\"name\":\"windows_i686_msvc\",\"req\":\"^0.48.5\",\"target\":\"cfg(all(target_arch = \\\"x86\\\", target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"},{\"name\":\"windows_x86_64_gnu\",\"req\":\"^0.48.5\",\"target\":\"cfg(all(target_arch = \\\"x86_64\\\", target_env = \\\"gnu\\\", not(target_abi = \\\"llvm\\\"), not(windows_raw_dylib)))\"},{\"name\":\"windows_x86_64_gnullvm\",\"req\":\"^0.48.5\",\"target\":\"x86_64-pc-windows-gnullvm\"},{\"name\":\"windows_x86_64_msvc\",\"req\":\"^0.48.5\",\"target\":\"cfg(all(target_arch = \\\"x86_64\\\", target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"}],\"features\":{}}", "windows-targets_0.52.6": "{\"dependencies\":[{\"name\":\"windows_aarch64_gnullvm\",\"req\":\"^0.52.6\",\"target\":\"aarch64-pc-windows-gnullvm\"},{\"name\":\"windows_aarch64_msvc\",\"req\":\"^0.52.6\",\"target\":\"cfg(all(target_arch = \\\"aarch64\\\", target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"},{\"name\":\"windows_i686_gnu\",\"req\":\"^0.52.6\",\"target\":\"cfg(all(target_arch = \\\"x86\\\", target_env = \\\"gnu\\\", not(target_abi = \\\"llvm\\\"), not(windows_raw_dylib)))\"},{\"name\":\"windows_i686_gnullvm\",\"req\":\"^0.52.6\",\"target\":\"i686-pc-windows-gnullvm\"},{\"name\":\"windows_i686_msvc\",\"req\":\"^0.52.6\",\"target\":\"cfg(all(target_arch = \\\"x86\\\", target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"},{\"name\":\"windows_x86_64_gnu\",\"req\":\"^0.52.6\",\"target\":\"cfg(all(target_arch = \\\"x86_64\\\", target_env = \\\"gnu\\\", not(target_abi = \\\"llvm\\\"), not(windows_raw_dylib)))\"},{\"name\":\"windows_x86_64_gnullvm\",\"req\":\"^0.52.6\",\"target\":\"x86_64-pc-windows-gnullvm\"},{\"name\":\"windows_x86_64_msvc\",\"req\":\"^0.52.6\",\"target\":\"cfg(all(any(target_arch = \\\"x86_64\\\", target_arch = \\\"arm64ec\\\"), target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"}],\"features\":{}}", - "windows-targets_0.53.2": "{\"dependencies\":[{\"name\":\"windows_aarch64_gnullvm\",\"req\":\"^0.53.0\",\"target\":\"aarch64-pc-windows-gnullvm\"},{\"name\":\"windows_aarch64_msvc\",\"req\":\"^0.53.0\",\"target\":\"cfg(all(target_arch = \\\"aarch64\\\", target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"},{\"name\":\"windows_i686_gnu\",\"req\":\"^0.53.0\",\"target\":\"cfg(all(target_arch = \\\"x86\\\", target_env = \\\"gnu\\\", not(target_abi = \\\"llvm\\\"), not(windows_raw_dylib)))\"},{\"name\":\"windows_i686_gnullvm\",\"req\":\"^0.53.0\",\"target\":\"i686-pc-windows-gnullvm\"},{\"name\":\"windows_i686_msvc\",\"req\":\"^0.53.0\",\"target\":\"cfg(all(target_arch = \\\"x86\\\", target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"},{\"name\":\"windows_x86_64_gnu\",\"req\":\"^0.53.0\",\"target\":\"cfg(all(target_arch = \\\"x86_64\\\", target_env = \\\"gnu\\\", not(target_abi = \\\"llvm\\\"), not(windows_raw_dylib)))\"},{\"name\":\"windows_x86_64_gnullvm\",\"req\":\"^0.53.0\",\"target\":\"x86_64-pc-windows-gnullvm\"},{\"name\":\"windows_x86_64_msvc\",\"req\":\"^0.53.0\",\"target\":\"cfg(all(any(target_arch = \\\"x86_64\\\", target_arch = \\\"arm64ec\\\"), target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"}],\"features\":{}}", - "windows-threading_0.1.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.1.1\"}],\"features\":{}}", + "windows-targets_0.53.5": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.2.1\",\"target\":\"cfg(windows_raw_dylib)\"},{\"name\":\"windows_aarch64_gnullvm\",\"req\":\"^0.53.0\",\"target\":\"aarch64-pc-windows-gnullvm\"},{\"name\":\"windows_aarch64_msvc\",\"req\":\"^0.53.0\",\"target\":\"cfg(all(target_arch = \\\"aarch64\\\", target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"},{\"name\":\"windows_i686_gnu\",\"req\":\"^0.53.0\",\"target\":\"cfg(all(target_arch = \\\"x86\\\", target_env = \\\"gnu\\\", not(target_abi = \\\"llvm\\\"), not(windows_raw_dylib)))\"},{\"name\":\"windows_i686_gnullvm\",\"req\":\"^0.53.0\",\"target\":\"i686-pc-windows-gnullvm\"},{\"name\":\"windows_i686_msvc\",\"req\":\"^0.53.0\",\"target\":\"cfg(all(target_arch = \\\"x86\\\", target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"},{\"name\":\"windows_x86_64_gnu\",\"req\":\"^0.53.0\",\"target\":\"cfg(all(target_arch = \\\"x86_64\\\", target_env = \\\"gnu\\\", not(target_abi = \\\"llvm\\\"), not(windows_raw_dylib)))\"},{\"name\":\"windows_x86_64_gnullvm\",\"req\":\"^0.53.0\",\"target\":\"x86_64-pc-windows-gnullvm\"},{\"name\":\"windows_x86_64_msvc\",\"req\":\"^0.53.0\",\"target\":\"cfg(all(any(target_arch = \\\"x86_64\\\", target_arch = \\\"arm64ec\\\"), target_env = \\\"msvc\\\", not(windows_raw_dylib)))\"}],\"features\":{}}", + "windows-threading_0.2.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.2.1\"}],\"features\":{}}", "windows_0.58.0": "{\"dependencies\":[{\"name\":\"windows-core\",\"req\":\"^0.58.0\"},{\"name\":\"windows-targets\",\"req\":\"^0.52.6\"}],\"features\":{\"AI\":[\"Foundation\"],\"AI_MachineLearning\":[\"AI\"],\"ApplicationModel\":[\"Foundation\"],\"ApplicationModel_Activation\":[\"ApplicationModel\"],\"ApplicationModel_AppExtensions\":[\"ApplicationModel\"],\"ApplicationModel_AppService\":[\"ApplicationModel\"],\"ApplicationModel_Appointments\":[\"ApplicationModel\"],\"ApplicationModel_Appointments_AppointmentsProvider\":[\"ApplicationModel_Appointments\"],\"ApplicationModel_Appointments_DataProvider\":[\"ApplicationModel_Appointments\"],\"ApplicationModel_Background\":[\"ApplicationModel\"],\"ApplicationModel_Calls\":[\"ApplicationModel\"],\"ApplicationModel_Calls_Background\":[\"ApplicationModel_Calls\"],\"ApplicationModel_Calls_Provider\":[\"ApplicationModel_Calls\"],\"ApplicationModel_Chat\":[\"ApplicationModel\"],\"ApplicationModel_CommunicationBlocking\":[\"ApplicationModel\"],\"ApplicationModel_Contacts\":[\"ApplicationModel\"],\"ApplicationModel_Contacts_DataProvider\":[\"ApplicationModel_Contacts\"],\"ApplicationModel_Contacts_Provider\":[\"ApplicationModel_Contacts\"],\"ApplicationModel_ConversationalAgent\":[\"ApplicationModel\"],\"ApplicationModel_Core\":[\"ApplicationModel\"],\"ApplicationModel_DataTransfer\":[\"ApplicationModel\"],\"ApplicationModel_DataTransfer_DragDrop\":[\"ApplicationModel_DataTransfer\"],\"ApplicationModel_DataTransfer_DragDrop_Core\":[\"ApplicationModel_DataTransfer_DragDrop\"],\"ApplicationModel_DataTransfer_ShareTarget\":[\"ApplicationModel_DataTransfer\"],\"ApplicationModel_Email\":[\"ApplicationModel\"],\"ApplicationModel_Email_DataProvider\":[\"ApplicationModel_Email\"],\"ApplicationModel_ExtendedExecution\":[\"ApplicationModel\"],\"ApplicationModel_ExtendedExecution_Foreground\":[\"ApplicationModel_ExtendedExecution\"],\"ApplicationModel_Holographic\":[\"ApplicationModel\"],\"ApplicationModel_LockScreen\":[\"ApplicationModel\"],\"ApplicationModel_PackageExtensions\":[\"ApplicationModel\"],\"ApplicationModel_Payments\":[\"ApplicationModel\"],\"ApplicationModel_Payments_Provider\":[\"ApplicationModel_Payments\"],\"ApplicationModel_Preview\":[\"ApplicationModel\"],\"ApplicationModel_Preview_Holographic\":[\"ApplicationModel_Preview\"],\"ApplicationModel_Preview_InkWorkspace\":[\"ApplicationModel_Preview\"],\"ApplicationModel_Preview_Notes\":[\"ApplicationModel_Preview\"],\"ApplicationModel_Resources\":[\"ApplicationModel\"],\"ApplicationModel_Resources_Core\":[\"ApplicationModel_Resources\"],\"ApplicationModel_Resources_Management\":[\"ApplicationModel_Resources\"],\"ApplicationModel_Search\":[\"ApplicationModel\"],\"ApplicationModel_Search_Core\":[\"ApplicationModel_Search\"],\"ApplicationModel_UserActivities\":[\"ApplicationModel\"],\"ApplicationModel_UserActivities_Core\":[\"ApplicationModel_UserActivities\"],\"ApplicationModel_UserDataAccounts\":[\"ApplicationModel\"],\"ApplicationModel_UserDataAccounts_Provider\":[\"ApplicationModel_UserDataAccounts\"],\"ApplicationModel_UserDataAccounts_SystemAccess\":[\"ApplicationModel_UserDataAccounts\"],\"ApplicationModel_UserDataTasks\":[\"ApplicationModel\"],\"ApplicationModel_UserDataTasks_DataProvider\":[\"ApplicationModel_UserDataTasks\"],\"ApplicationModel_VoiceCommands\":[\"ApplicationModel\"],\"ApplicationModel_Wallet\":[\"ApplicationModel\"],\"ApplicationModel_Wallet_System\":[\"ApplicationModel_Wallet\"],\"Data\":[\"Foundation\"],\"Data_Html\":[\"Data\"],\"Data_Json\":[\"Data\"],\"Data_Pdf\":[\"Data\"],\"Data_Text\":[\"Data\"],\"Data_Xml\":[\"Data\"],\"Data_Xml_Dom\":[\"Data_Xml\"],\"Data_Xml_Xsl\":[\"Data_Xml\"],\"Devices\":[\"Foundation\"],\"Devices_Adc\":[\"Devices\"],\"Devices_Adc_Provider\":[\"Devices_Adc\"],\"Devices_Background\":[\"Devices\"],\"Devices_Bluetooth\":[\"Devices\"],\"Devices_Bluetooth_Advertisement\":[\"Devices_Bluetooth\"],\"Devices_Bluetooth_Background\":[\"Devices_Bluetooth\"],\"Devices_Bluetooth_GenericAttributeProfile\":[\"Devices_Bluetooth\"],\"Devices_Bluetooth_Rfcomm\":[\"Devices_Bluetooth\"],\"Devices_Custom\":[\"Devices\"],\"Devices_Display\":[\"Devices\"],\"Devices_Display_Core\":[\"Devices_Display\"],\"Devices_Enumeration\":[\"Devices\"],\"Devices_Enumeration_Pnp\":[\"Devices_Enumeration\"],\"Devices_Geolocation\":[\"Devices\"],\"Devices_Geolocation_Geofencing\":[\"Devices_Geolocation\"],\"Devices_Geolocation_Provider\":[\"Devices_Geolocation\"],\"Devices_Gpio\":[\"Devices\"],\"Devices_Gpio_Provider\":[\"Devices_Gpio\"],\"Devices_Haptics\":[\"Devices\"],\"Devices_HumanInterfaceDevice\":[\"Devices\"],\"Devices_I2c\":[\"Devices\"],\"Devices_I2c_Provider\":[\"Devices_I2c\"],\"Devices_Input\":[\"Devices\"],\"Devices_Input_Preview\":[\"Devices_Input\"],\"Devices_Lights\":[\"Devices\"],\"Devices_Lights_Effects\":[\"Devices_Lights\"],\"Devices_Midi\":[\"Devices\"],\"Devices_PointOfService\":[\"Devices\"],\"Devices_PointOfService_Provider\":[\"Devices_PointOfService\"],\"Devices_Portable\":[\"Devices\"],\"Devices_Power\":[\"Devices\"],\"Devices_Printers\":[\"Devices\"],\"Devices_Printers_Extensions\":[\"Devices_Printers\"],\"Devices_Pwm\":[\"Devices\"],\"Devices_Pwm_Provider\":[\"Devices_Pwm\"],\"Devices_Radios\":[\"Devices\"],\"Devices_Scanners\":[\"Devices\"],\"Devices_Sensors\":[\"Devices\"],\"Devices_Sensors_Custom\":[\"Devices_Sensors\"],\"Devices_SerialCommunication\":[\"Devices\"],\"Devices_SmartCards\":[\"Devices\"],\"Devices_Sms\":[\"Devices\"],\"Devices_Spi\":[\"Devices\"],\"Devices_Spi_Provider\":[\"Devices_Spi\"],\"Devices_Usb\":[\"Devices\"],\"Devices_WiFi\":[\"Devices\"],\"Devices_WiFiDirect\":[\"Devices\"],\"Devices_WiFiDirect_Services\":[\"Devices_WiFiDirect\"],\"Embedded\":[\"Foundation\"],\"Embedded_DeviceLockdown\":[\"Embedded\"],\"Foundation\":[],\"Foundation_Collections\":[\"Foundation\"],\"Foundation_Diagnostics\":[\"Foundation\"],\"Foundation_Metadata\":[\"Foundation\"],\"Foundation_Numerics\":[\"Foundation\"],\"Gaming\":[\"Foundation\"],\"Gaming_Input\":[\"Gaming\"],\"Gaming_Input_Custom\":[\"Gaming_Input\"],\"Gaming_Input_ForceFeedback\":[\"Gaming_Input\"],\"Gaming_Input_Preview\":[\"Gaming_Input\"],\"Gaming_Preview\":[\"Gaming\"],\"Gaming_Preview_GamesEnumeration\":[\"Gaming_Preview\"],\"Gaming_UI\":[\"Gaming\"],\"Gaming_XboxLive\":[\"Gaming\"],\"Gaming_XboxLive_Storage\":[\"Gaming_XboxLive\"],\"Globalization\":[\"Foundation\"],\"Globalization_Collation\":[\"Globalization\"],\"Globalization_DateTimeFormatting\":[\"Globalization\"],\"Globalization_Fonts\":[\"Globalization\"],\"Globalization_NumberFormatting\":[\"Globalization\"],\"Globalization_PhoneNumberFormatting\":[\"Globalization\"],\"Graphics\":[\"Foundation\"],\"Graphics_Capture\":[\"Graphics\"],\"Graphics_DirectX\":[\"Graphics\"],\"Graphics_DirectX_Direct3D11\":[\"Graphics_DirectX\"],\"Graphics_Display\":[\"Graphics\"],\"Graphics_Display_Core\":[\"Graphics_Display\"],\"Graphics_Effects\":[\"Graphics\"],\"Graphics_Holographic\":[\"Graphics\"],\"Graphics_Imaging\":[\"Graphics\"],\"Graphics_Printing\":[\"Graphics\"],\"Graphics_Printing3D\":[\"Graphics\"],\"Graphics_Printing_OptionDetails\":[\"Graphics_Printing\"],\"Graphics_Printing_PrintSupport\":[\"Graphics_Printing\"],\"Graphics_Printing_PrintTicket\":[\"Graphics_Printing\"],\"Graphics_Printing_Workflow\":[\"Graphics_Printing\"],\"Management\":[\"Foundation\"],\"Management_Core\":[\"Management\"],\"Management_Deployment\":[\"Management\"],\"Management_Deployment_Preview\":[\"Management_Deployment\"],\"Management_Policies\":[\"Management\"],\"Management_Setup\":[\"Management\"],\"Management_Update\":[\"Management\"],\"Management_Workplace\":[\"Management\"],\"Media\":[\"Foundation\"],\"Media_AppBroadcasting\":[\"Media\"],\"Media_AppRecording\":[\"Media\"],\"Media_Audio\":[\"Media\"],\"Media_Capture\":[\"Media\"],\"Media_Capture_Core\":[\"Media_Capture\"],\"Media_Capture_Frames\":[\"Media_Capture\"],\"Media_Casting\":[\"Media\"],\"Media_ClosedCaptioning\":[\"Media\"],\"Media_ContentRestrictions\":[\"Media\"],\"Media_Control\":[\"Media\"],\"Media_Core\":[\"Media\"],\"Media_Core_Preview\":[\"Media_Core\"],\"Media_Devices\":[\"Media\"],\"Media_Devices_Core\":[\"Media_Devices\"],\"Media_DialProtocol\":[\"Media\"],\"Media_Editing\":[\"Media\"],\"Media_Effects\":[\"Media\"],\"Media_FaceAnalysis\":[\"Media\"],\"Media_Import\":[\"Media\"],\"Media_MediaProperties\":[\"Media\"],\"Media_Miracast\":[\"Media\"],\"Media_Ocr\":[\"Media\"],\"Media_PlayTo\":[\"Media\"],\"Media_Playback\":[\"Media\"],\"Media_Playlists\":[\"Media\"],\"Media_Protection\":[\"Media\"],\"Media_Protection_PlayReady\":[\"Media_Protection\"],\"Media_Render\":[\"Media\"],\"Media_SpeechRecognition\":[\"Media\"],\"Media_SpeechSynthesis\":[\"Media\"],\"Media_Streaming\":[\"Media\"],\"Media_Streaming_Adaptive\":[\"Media_Streaming\"],\"Media_Transcoding\":[\"Media\"],\"Networking\":[\"Foundation\"],\"Networking_BackgroundTransfer\":[\"Networking\"],\"Networking_Connectivity\":[\"Networking\"],\"Networking_NetworkOperators\":[\"Networking\"],\"Networking_Proximity\":[\"Networking\"],\"Networking_PushNotifications\":[\"Networking\"],\"Networking_ServiceDiscovery\":[\"Networking\"],\"Networking_ServiceDiscovery_Dnssd\":[\"Networking_ServiceDiscovery\"],\"Networking_Sockets\":[\"Networking\"],\"Networking_Vpn\":[\"Networking\"],\"Networking_XboxLive\":[\"Networking\"],\"Perception\":[\"Foundation\"],\"Perception_Automation\":[\"Perception\"],\"Perception_Automation_Core\":[\"Perception_Automation\"],\"Perception_People\":[\"Perception\"],\"Perception_Spatial\":[\"Perception\"],\"Perception_Spatial_Preview\":[\"Perception_Spatial\"],\"Perception_Spatial_Surfaces\":[\"Perception_Spatial\"],\"Phone\":[\"Foundation\"],\"Phone_ApplicationModel\":[\"Phone\"],\"Phone_Devices\":[\"Phone\"],\"Phone_Devices_Notification\":[\"Phone_Devices\"],\"Phone_Devices_Power\":[\"Phone_Devices\"],\"Phone_Management\":[\"Phone\"],\"Phone_Management_Deployment\":[\"Phone_Management\"],\"Phone_Media\":[\"Phone\"],\"Phone_Media_Devices\":[\"Phone_Media\"],\"Phone_Notification\":[\"Phone\"],\"Phone_Notification_Management\":[\"Phone_Notification\"],\"Phone_PersonalInformation\":[\"Phone\"],\"Phone_PersonalInformation_Provisioning\":[\"Phone_PersonalInformation\"],\"Phone_Speech\":[\"Phone\"],\"Phone_Speech_Recognition\":[\"Phone_Speech\"],\"Phone_StartScreen\":[\"Phone\"],\"Phone_System\":[\"Phone\"],\"Phone_System_Power\":[\"Phone_System\"],\"Phone_System_Profile\":[\"Phone_System\"],\"Phone_System_UserProfile\":[\"Phone_System\"],\"Phone_System_UserProfile_GameServices\":[\"Phone_System_UserProfile\"],\"Phone_System_UserProfile_GameServices_Core\":[\"Phone_System_UserProfile_GameServices\"],\"Phone_UI\":[\"Phone\"],\"Phone_UI_Input\":[\"Phone_UI\"],\"Security\":[\"Foundation\"],\"Security_Authentication\":[\"Security\"],\"Security_Authentication_Identity\":[\"Security_Authentication\"],\"Security_Authentication_Identity_Core\":[\"Security_Authentication_Identity\"],\"Security_Authentication_OnlineId\":[\"Security_Authentication\"],\"Security_Authentication_Web\":[\"Security_Authentication\"],\"Security_Authentication_Web_Core\":[\"Security_Authentication_Web\"],\"Security_Authentication_Web_Provider\":[\"Security_Authentication_Web\"],\"Security_Authorization\":[\"Security\"],\"Security_Authorization_AppCapabilityAccess\":[\"Security_Authorization\"],\"Security_Credentials\":[\"Security\"],\"Security_Credentials_UI\":[\"Security_Credentials\"],\"Security_Cryptography\":[\"Security\"],\"Security_Cryptography_Certificates\":[\"Security_Cryptography\"],\"Security_Cryptography_Core\":[\"Security_Cryptography\"],\"Security_Cryptography_DataProtection\":[\"Security_Cryptography\"],\"Security_DataProtection\":[\"Security\"],\"Security_EnterpriseData\":[\"Security\"],\"Security_ExchangeActiveSyncProvisioning\":[\"Security\"],\"Security_Isolation\":[\"Security\"],\"Services\":[\"Foundation\"],\"Services_Maps\":[\"Services\"],\"Services_Maps_Guidance\":[\"Services_Maps\"],\"Services_Maps_LocalSearch\":[\"Services_Maps\"],\"Services_Maps_OfflineMaps\":[\"Services_Maps\"],\"Services_Store\":[\"Services\"],\"Services_TargetedContent\":[\"Services\"],\"Storage\":[\"Foundation\"],\"Storage_AccessCache\":[\"Storage\"],\"Storage_BulkAccess\":[\"Storage\"],\"Storage_Compression\":[\"Storage\"],\"Storage_FileProperties\":[\"Storage\"],\"Storage_Pickers\":[\"Storage\"],\"Storage_Pickers_Provider\":[\"Storage_Pickers\"],\"Storage_Provider\":[\"Storage\"],\"Storage_Search\":[\"Storage\"],\"Storage_Streams\":[\"Storage\"],\"System\":[\"Foundation\"],\"System_Diagnostics\":[\"System\"],\"System_Diagnostics_DevicePortal\":[\"System_Diagnostics\"],\"System_Diagnostics_Telemetry\":[\"System_Diagnostics\"],\"System_Diagnostics_TraceReporting\":[\"System_Diagnostics\"],\"System_Display\":[\"System\"],\"System_Implementation\":[\"System\"],\"System_Implementation_FileExplorer\":[\"System_Implementation\"],\"System_Inventory\":[\"System\"],\"System_Power\":[\"System\"],\"System_Profile\":[\"System\"],\"System_Profile_SystemManufacturers\":[\"System_Profile\"],\"System_RemoteDesktop\":[\"System\"],\"System_RemoteDesktop_Input\":[\"System_RemoteDesktop\"],\"System_RemoteDesktop_Provider\":[\"System_RemoteDesktop\"],\"System_RemoteSystems\":[\"System\"],\"System_Threading\":[\"System\"],\"System_Threading_Core\":[\"System_Threading\"],\"System_Update\":[\"System\"],\"System_UserProfile\":[\"System\"],\"UI\":[\"Foundation\"],\"UI_Accessibility\":[\"UI\"],\"UI_ApplicationSettings\":[\"UI\"],\"UI_Composition\":[\"UI\"],\"UI_Composition_Core\":[\"UI_Composition\"],\"UI_Composition_Desktop\":[\"UI_Composition\"],\"UI_Composition_Diagnostics\":[\"UI_Composition\"],\"UI_Composition_Effects\":[\"UI_Composition\"],\"UI_Composition_Interactions\":[\"UI_Composition\"],\"UI_Composition_Scenes\":[\"UI_Composition\"],\"UI_Core\":[\"UI\"],\"UI_Core_AnimationMetrics\":[\"UI_Core\"],\"UI_Core_Preview\":[\"UI_Core\"],\"UI_Input\":[\"UI\"],\"UI_Input_Core\":[\"UI_Input\"],\"UI_Input_Inking\":[\"UI_Input\"],\"UI_Input_Inking_Analysis\":[\"UI_Input_Inking\"],\"UI_Input_Inking_Core\":[\"UI_Input_Inking\"],\"UI_Input_Inking_Preview\":[\"UI_Input_Inking\"],\"UI_Input_Preview\":[\"UI_Input\"],\"UI_Input_Preview_Injection\":[\"UI_Input_Preview\"],\"UI_Input_Spatial\":[\"UI_Input\"],\"UI_Notifications\":[\"UI\"],\"UI_Notifications_Management\":[\"UI_Notifications\"],\"UI_Notifications_Preview\":[\"UI_Notifications\"],\"UI_Popups\":[\"UI\"],\"UI_Shell\":[\"UI\"],\"UI_StartScreen\":[\"UI\"],\"UI_Text\":[\"UI\"],\"UI_Text_Core\":[\"UI_Text\"],\"UI_UIAutomation\":[\"UI\"],\"UI_UIAutomation_Core\":[\"UI_UIAutomation\"],\"UI_ViewManagement\":[\"UI\"],\"UI_ViewManagement_Core\":[\"UI_ViewManagement\"],\"UI_WebUI\":[\"UI\"],\"UI_WebUI_Core\":[\"UI_WebUI\"],\"UI_WindowManagement\":[\"UI\"],\"UI_WindowManagement_Preview\":[\"UI_WindowManagement\"],\"Wdk\":[\"Win32_Foundation\"],\"Wdk_Devices\":[\"Wdk\"],\"Wdk_Devices_Bluetooth\":[\"Wdk_Devices\"],\"Wdk_Devices_HumanInterfaceDevice\":[\"Wdk_Devices\"],\"Wdk_Foundation\":[\"Wdk\"],\"Wdk_Graphics\":[\"Wdk\"],\"Wdk_Graphics_Direct3D\":[\"Wdk_Graphics\"],\"Wdk_NetworkManagement\":[\"Wdk\"],\"Wdk_NetworkManagement_Ndis\":[\"Wdk_NetworkManagement\"],\"Wdk_NetworkManagement_WindowsFilteringPlatform\":[\"Wdk_NetworkManagement\"],\"Wdk_Storage\":[\"Wdk\"],\"Wdk_Storage_FileSystem\":[\"Wdk_Storage\"],\"Wdk_Storage_FileSystem_Minifilters\":[\"Wdk_Storage_FileSystem\"],\"Wdk_System\":[\"Wdk\"],\"Wdk_System_IO\":[\"Wdk_System\"],\"Wdk_System_Memory\":[\"Wdk_System\"],\"Wdk_System_OfflineRegistry\":[\"Wdk_System\"],\"Wdk_System_Registry\":[\"Wdk_System\"],\"Wdk_System_SystemInformation\":[\"Wdk_System\"],\"Wdk_System_SystemServices\":[\"Wdk_System\"],\"Wdk_System_Threading\":[\"Wdk_System\"],\"Web\":[\"Foundation\"],\"Web_AtomPub\":[\"Web\"],\"Web_Http\":[\"Web\"],\"Web_Http_Diagnostics\":[\"Web_Http\"],\"Web_Http_Filters\":[\"Web_Http\"],\"Web_Http_Headers\":[\"Web_Http\"],\"Web_Syndication\":[\"Web\"],\"Web_UI\":[\"Web\"],\"Web_UI_Interop\":[\"Web_UI\"],\"Win32\":[\"Win32_Foundation\"],\"Win32_AI\":[\"Win32\"],\"Win32_AI_MachineLearning\":[\"Win32_AI\"],\"Win32_AI_MachineLearning_DirectML\":[\"Win32_AI_MachineLearning\"],\"Win32_AI_MachineLearning_WinML\":[\"Win32_AI_MachineLearning\"],\"Win32_Data\":[\"Win32\"],\"Win32_Data_HtmlHelp\":[\"Win32_Data\"],\"Win32_Data_RightsManagement\":[\"Win32_Data\"],\"Win32_Data_Xml\":[\"Win32_Data\"],\"Win32_Data_Xml_MsXml\":[\"Win32_Data_Xml\"],\"Win32_Data_Xml_XmlLite\":[\"Win32_Data_Xml\"],\"Win32_Devices\":[\"Win32\"],\"Win32_Devices_AllJoyn\":[\"Win32_Devices\"],\"Win32_Devices_BiometricFramework\":[\"Win32_Devices\"],\"Win32_Devices_Bluetooth\":[\"Win32_Devices\"],\"Win32_Devices_Communication\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAccess\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAndDriverInstallation\":[\"Win32_Devices\"],\"Win32_Devices_DeviceQuery\":[\"Win32_Devices\"],\"Win32_Devices_Display\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration_Pnp\":[\"Win32_Devices_Enumeration\"],\"Win32_Devices_Fax\":[\"Win32_Devices\"],\"Win32_Devices_FunctionDiscovery\":[\"Win32_Devices\"],\"Win32_Devices_Geolocation\":[\"Win32_Devices\"],\"Win32_Devices_HumanInterfaceDevice\":[\"Win32_Devices\"],\"Win32_Devices_ImageAcquisition\":[\"Win32_Devices\"],\"Win32_Devices_PortableDevices\":[\"Win32_Devices\"],\"Win32_Devices_Properties\":[\"Win32_Devices\"],\"Win32_Devices_Pwm\":[\"Win32_Devices\"],\"Win32_Devices_Sensors\":[\"Win32_Devices\"],\"Win32_Devices_SerialCommunication\":[\"Win32_Devices\"],\"Win32_Devices_Tapi\":[\"Win32_Devices\"],\"Win32_Devices_Usb\":[\"Win32_Devices\"],\"Win32_Devices_WebServicesOnDevices\":[\"Win32_Devices\"],\"Win32_Foundation\":[\"Win32\"],\"Win32_Gaming\":[\"Win32\"],\"Win32_Globalization\":[\"Win32\"],\"Win32_Graphics\":[\"Win32\"],\"Win32_Graphics_CompositionSwapchain\":[\"Win32_Graphics\"],\"Win32_Graphics_DXCore\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct2D\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct2D_Common\":[\"Win32_Graphics_Direct2D\"],\"Win32_Graphics_Direct3D\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D10\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D11\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D11on12\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D12\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D9\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D9on12\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D_Dxc\":[\"Win32_Graphics_Direct3D\"],\"Win32_Graphics_Direct3D_Fxc\":[\"Win32_Graphics_Direct3D\"],\"Win32_Graphics_DirectComposition\":[\"Win32_Graphics\"],\"Win32_Graphics_DirectDraw\":[\"Win32_Graphics\"],\"Win32_Graphics_DirectManipulation\":[\"Win32_Graphics\"],\"Win32_Graphics_DirectWrite\":[\"Win32_Graphics\"],\"Win32_Graphics_Dwm\":[\"Win32_Graphics\"],\"Win32_Graphics_Dxgi\":[\"Win32_Graphics\"],\"Win32_Graphics_Dxgi_Common\":[\"Win32_Graphics_Dxgi\"],\"Win32_Graphics_Gdi\":[\"Win32_Graphics\"],\"Win32_Graphics_GdiPlus\":[\"Win32_Graphics\"],\"Win32_Graphics_Hlsl\":[\"Win32_Graphics\"],\"Win32_Graphics_Imaging\":[\"Win32_Graphics\"],\"Win32_Graphics_Imaging_D2D\":[\"Win32_Graphics_Imaging\"],\"Win32_Graphics_OpenGL\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing_PrintTicket\":[\"Win32_Graphics_Printing\"],\"Win32_Management\":[\"Win32\"],\"Win32_Management_MobileDeviceManagementRegistration\":[\"Win32_Management\"],\"Win32_Media\":[\"Win32\"],\"Win32_Media_Audio\":[\"Win32_Media\"],\"Win32_Media_Audio_Apo\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_DirectMusic\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_DirectSound\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_Endpoints\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_XAudio2\":[\"Win32_Media_Audio\"],\"Win32_Media_DeviceManager\":[\"Win32_Media\"],\"Win32_Media_DirectShow\":[\"Win32_Media\"],\"Win32_Media_DirectShow_Tv\":[\"Win32_Media_DirectShow\"],\"Win32_Media_DirectShow_Xml\":[\"Win32_Media_DirectShow\"],\"Win32_Media_DxMediaObjects\":[\"Win32_Media\"],\"Win32_Media_KernelStreaming\":[\"Win32_Media\"],\"Win32_Media_LibrarySharingServices\":[\"Win32_Media\"],\"Win32_Media_MediaFoundation\":[\"Win32_Media\"],\"Win32_Media_MediaPlayer\":[\"Win32_Media\"],\"Win32_Media_Multimedia\":[\"Win32_Media\"],\"Win32_Media_PictureAcquisition\":[\"Win32_Media\"],\"Win32_Media_Speech\":[\"Win32_Media\"],\"Win32_Media_Streaming\":[\"Win32_Media\"],\"Win32_Media_WindowsMediaFormat\":[\"Win32_Media\"],\"Win32_NetworkManagement\":[\"Win32\"],\"Win32_NetworkManagement_Dhcp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Dns\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_InternetConnectionWizard\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_IpHelper\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_MobileBroadband\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Multicast\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Ndis\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetBios\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetManagement\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetShell\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkDiagnosticsFramework\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkPolicyServer\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_P2P\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_QoS\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Rras\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Snmp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WNet\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WebDav\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WiFi\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectNow\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectionManager\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFilteringPlatform\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFirewall\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsNetworkVirtualization\":[\"Win32_NetworkManagement\"],\"Win32_Networking\":[\"Win32\"],\"Win32_Networking_ActiveDirectory\":[\"Win32_Networking\"],\"Win32_Networking_BackgroundIntelligentTransferService\":[\"Win32_Networking\"],\"Win32_Networking_Clustering\":[\"Win32_Networking\"],\"Win32_Networking_HttpServer\":[\"Win32_Networking\"],\"Win32_Networking_Ldap\":[\"Win32_Networking\"],\"Win32_Networking_NetworkListManager\":[\"Win32_Networking\"],\"Win32_Networking_RemoteDifferentialCompression\":[\"Win32_Networking\"],\"Win32_Networking_WebSocket\":[\"Win32_Networking\"],\"Win32_Networking_WinHttp\":[\"Win32_Networking\"],\"Win32_Networking_WinInet\":[\"Win32_Networking\"],\"Win32_Networking_WinSock\":[\"Win32_Networking\"],\"Win32_Networking_WindowsWebServices\":[\"Win32_Networking\"],\"Win32_Security\":[\"Win32\"],\"Win32_Security_AppLocker\":[\"Win32_Security\"],\"Win32_Security_Authentication\":[\"Win32_Security\"],\"Win32_Security_Authentication_Identity\":[\"Win32_Security_Authentication\"],\"Win32_Security_Authentication_Identity_Provider\":[\"Win32_Security_Authentication_Identity\"],\"Win32_Security_Authorization\":[\"Win32_Security\"],\"Win32_Security_Authorization_UI\":[\"Win32_Security_Authorization\"],\"Win32_Security_ConfigurationSnapin\":[\"Win32_Security\"],\"Win32_Security_Credentials\":[\"Win32_Security\"],\"Win32_Security_Cryptography\":[\"Win32_Security\"],\"Win32_Security_Cryptography_Catalog\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Certificates\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Sip\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_UI\":[\"Win32_Security_Cryptography\"],\"Win32_Security_DiagnosticDataQuery\":[\"Win32_Security\"],\"Win32_Security_DirectoryServices\":[\"Win32_Security\"],\"Win32_Security_EnterpriseData\":[\"Win32_Security\"],\"Win32_Security_ExtensibleAuthenticationProtocol\":[\"Win32_Security\"],\"Win32_Security_Isolation\":[\"Win32_Security\"],\"Win32_Security_LicenseProtection\":[\"Win32_Security\"],\"Win32_Security_NetworkAccessProtection\":[\"Win32_Security\"],\"Win32_Security_Tpm\":[\"Win32_Security\"],\"Win32_Security_WinTrust\":[\"Win32_Security\"],\"Win32_Security_WinWlx\":[\"Win32_Security\"],\"Win32_Storage\":[\"Win32\"],\"Win32_Storage_Cabinets\":[\"Win32_Storage\"],\"Win32_Storage_CloudFilters\":[\"Win32_Storage\"],\"Win32_Storage_Compression\":[\"Win32_Storage\"],\"Win32_Storage_DataDeduplication\":[\"Win32_Storage\"],\"Win32_Storage_DistributedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_EnhancedStorage\":[\"Win32_Storage\"],\"Win32_Storage_FileHistory\":[\"Win32_Storage\"],\"Win32_Storage_FileServerResourceManager\":[\"Win32_Storage\"],\"Win32_Storage_FileSystem\":[\"Win32_Storage\"],\"Win32_Storage_Imapi\":[\"Win32_Storage\"],\"Win32_Storage_IndexServer\":[\"Win32_Storage\"],\"Win32_Storage_InstallableFileSystems\":[\"Win32_Storage\"],\"Win32_Storage_IscsiDisc\":[\"Win32_Storage\"],\"Win32_Storage_Jet\":[\"Win32_Storage\"],\"Win32_Storage_Nvme\":[\"Win32_Storage\"],\"Win32_Storage_OfflineFiles\":[\"Win32_Storage\"],\"Win32_Storage_OperationRecorder\":[\"Win32_Storage\"],\"Win32_Storage_Packaging\":[\"Win32_Storage\"],\"Win32_Storage_Packaging_Appx\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_Packaging_Opc\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_ProjectedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_StructuredStorage\":[\"Win32_Storage\"],\"Win32_Storage_Vhd\":[\"Win32_Storage\"],\"Win32_Storage_VirtualDiskService\":[\"Win32_Storage\"],\"Win32_Storage_Vss\":[\"Win32_Storage\"],\"Win32_Storage_Xps\":[\"Win32_Storage\"],\"Win32_Storage_Xps_Printing\":[\"Win32_Storage_Xps\"],\"Win32_System\":[\"Win32\"],\"Win32_System_AddressBook\":[\"Win32_System\"],\"Win32_System_Antimalware\":[\"Win32_System\"],\"Win32_System_ApplicationInstallationAndServicing\":[\"Win32_System\"],\"Win32_System_ApplicationVerifier\":[\"Win32_System\"],\"Win32_System_AssessmentTool\":[\"Win32_System\"],\"Win32_System_ClrHosting\":[\"Win32_System\"],\"Win32_System_Com\":[\"Win32_System\"],\"Win32_System_Com_CallObj\":[\"Win32_System_Com\"],\"Win32_System_Com_ChannelCredentials\":[\"Win32_System_Com\"],\"Win32_System_Com_Events\":[\"Win32_System_Com\"],\"Win32_System_Com_Marshal\":[\"Win32_System_Com\"],\"Win32_System_Com_StructuredStorage\":[\"Win32_System_Com\"],\"Win32_System_Com_UI\":[\"Win32_System_Com\"],\"Win32_System_Com_Urlmon\":[\"Win32_System_Com\"],\"Win32_System_ComponentServices\":[\"Win32_System\"],\"Win32_System_Console\":[\"Win32_System\"],\"Win32_System_Contacts\":[\"Win32_System\"],\"Win32_System_CorrelationVector\":[\"Win32_System\"],\"Win32_System_DataExchange\":[\"Win32_System\"],\"Win32_System_DeploymentServices\":[\"Win32_System\"],\"Win32_System_DesktopSharing\":[\"Win32_System\"],\"Win32_System_DeveloperLicensing\":[\"Win32_System\"],\"Win32_System_Diagnostics\":[\"Win32_System\"],\"Win32_System_Diagnostics_Ceip\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ClrProfiling\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug_ActiveScript\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Debug_Extensions\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Etw\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ProcessSnapshotting\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ToolHelp\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_TraceLogging\":[\"Win32_System_Diagnostics\"],\"Win32_System_DistributedTransactionCoordinator\":[\"Win32_System\"],\"Win32_System_Environment\":[\"Win32_System\"],\"Win32_System_ErrorReporting\":[\"Win32_System\"],\"Win32_System_EventCollector\":[\"Win32_System\"],\"Win32_System_EventLog\":[\"Win32_System\"],\"Win32_System_EventNotificationService\":[\"Win32_System\"],\"Win32_System_GroupPolicy\":[\"Win32_System\"],\"Win32_System_HostCompute\":[\"Win32_System\"],\"Win32_System_HostComputeNetwork\":[\"Win32_System\"],\"Win32_System_HostComputeSystem\":[\"Win32_System\"],\"Win32_System_Hypervisor\":[\"Win32_System\"],\"Win32_System_IO\":[\"Win32_System\"],\"Win32_System_Iis\":[\"Win32_System\"],\"Win32_System_Ioctl\":[\"Win32_System\"],\"Win32_System_JobObjects\":[\"Win32_System\"],\"Win32_System_Js\":[\"Win32_System\"],\"Win32_System_Kernel\":[\"Win32_System\"],\"Win32_System_LibraryLoader\":[\"Win32_System\"],\"Win32_System_Mailslots\":[\"Win32_System\"],\"Win32_System_Mapi\":[\"Win32_System\"],\"Win32_System_Memory\":[\"Win32_System\"],\"Win32_System_Memory_NonVolatile\":[\"Win32_System_Memory\"],\"Win32_System_MessageQueuing\":[\"Win32_System\"],\"Win32_System_MixedReality\":[\"Win32_System\"],\"Win32_System_Mmc\":[\"Win32_System\"],\"Win32_System_Ole\":[\"Win32_System\"],\"Win32_System_ParentalControls\":[\"Win32_System\"],\"Win32_System_PasswordManagement\":[\"Win32_System\"],\"Win32_System_Performance\":[\"Win32_System\"],\"Win32_System_Performance_HardwareCounterProfiling\":[\"Win32_System_Performance\"],\"Win32_System_Pipes\":[\"Win32_System\"],\"Win32_System_Power\":[\"Win32_System\"],\"Win32_System_ProcessStatus\":[\"Win32_System\"],\"Win32_System_RealTimeCommunications\":[\"Win32_System\"],\"Win32_System_Recovery\":[\"Win32_System\"],\"Win32_System_Registry\":[\"Win32_System\"],\"Win32_System_RemoteAssistance\":[\"Win32_System\"],\"Win32_System_RemoteDesktop\":[\"Win32_System\"],\"Win32_System_RemoteManagement\":[\"Win32_System\"],\"Win32_System_RestartManager\":[\"Win32_System\"],\"Win32_System_Restore\":[\"Win32_System\"],\"Win32_System_Rpc\":[\"Win32_System\"],\"Win32_System_Search\":[\"Win32_System\"],\"Win32_System_Search_Common\":[\"Win32_System_Search\"],\"Win32_System_SecurityCenter\":[\"Win32_System\"],\"Win32_System_ServerBackup\":[\"Win32_System\"],\"Win32_System_Services\":[\"Win32_System\"],\"Win32_System_SettingsManagementInfrastructure\":[\"Win32_System\"],\"Win32_System_SetupAndMigration\":[\"Win32_System\"],\"Win32_System_Shutdown\":[\"Win32_System\"],\"Win32_System_SideShow\":[\"Win32_System\"],\"Win32_System_StationsAndDesktops\":[\"Win32_System\"],\"Win32_System_SubsystemForLinux\":[\"Win32_System\"],\"Win32_System_SystemInformation\":[\"Win32_System\"],\"Win32_System_SystemServices\":[\"Win32_System\"],\"Win32_System_TaskScheduler\":[\"Win32_System\"],\"Win32_System_Threading\":[\"Win32_System\"],\"Win32_System_Time\":[\"Win32_System\"],\"Win32_System_TpmBaseServices\":[\"Win32_System\"],\"Win32_System_TransactionServer\":[\"Win32_System\"],\"Win32_System_UpdateAgent\":[\"Win32_System\"],\"Win32_System_UpdateAssessment\":[\"Win32_System\"],\"Win32_System_UserAccessLogging\":[\"Win32_System\"],\"Win32_System_Variant\":[\"Win32_System\"],\"Win32_System_VirtualDosMachines\":[\"Win32_System\"],\"Win32_System_WinRT\":[\"Win32_System\"],\"Win32_System_WinRT_AllJoyn\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Composition\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_CoreInputView\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Direct3D11\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Display\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Graphics\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Graphics_Capture\":[\"Win32_System_WinRT_Graphics\"],\"Win32_System_WinRT_Graphics_Direct2D\":[\"Win32_System_WinRT_Graphics\"],\"Win32_System_WinRT_Graphics_Imaging\":[\"Win32_System_WinRT_Graphics\"],\"Win32_System_WinRT_Holographic\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Isolation\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_ML\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Media\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Metadata\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Pdf\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Printing\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Shell\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Storage\":[\"Win32_System_WinRT\"],\"Win32_System_WindowsProgramming\":[\"Win32_System\"],\"Win32_System_WindowsSync\":[\"Win32_System\"],\"Win32_System_Wmi\":[\"Win32_System\"],\"Win32_UI\":[\"Win32\"],\"Win32_UI_Accessibility\":[\"Win32_UI\"],\"Win32_UI_Animation\":[\"Win32_UI\"],\"Win32_UI_ColorSystem\":[\"Win32_UI\"],\"Win32_UI_Controls\":[\"Win32_UI\"],\"Win32_UI_Controls_Dialogs\":[\"Win32_UI_Controls\"],\"Win32_UI_Controls_RichEdit\":[\"Win32_UI_Controls\"],\"Win32_UI_HiDpi\":[\"Win32_UI\"],\"Win32_UI_Input\":[\"Win32_UI\"],\"Win32_UI_Input_Ime\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Ink\":[\"Win32_UI_Input\"],\"Win32_UI_Input_KeyboardAndMouse\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Pointer\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Radial\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Touch\":[\"Win32_UI_Input\"],\"Win32_UI_Input_XboxController\":[\"Win32_UI_Input\"],\"Win32_UI_InteractionContext\":[\"Win32_UI\"],\"Win32_UI_LegacyWindowsEnvironmentFeatures\":[\"Win32_UI\"],\"Win32_UI_Magnification\":[\"Win32_UI\"],\"Win32_UI_Notifications\":[\"Win32_UI\"],\"Win32_UI_Ribbon\":[\"Win32_UI\"],\"Win32_UI_Shell\":[\"Win32_UI\"],\"Win32_UI_Shell_Common\":[\"Win32_UI_Shell\"],\"Win32_UI_Shell_PropertiesSystem\":[\"Win32_UI_Shell\"],\"Win32_UI_TabletPC\":[\"Win32_UI\"],\"Win32_UI_TextServices\":[\"Win32_UI\"],\"Win32_UI_WindowsAndMessaging\":[\"Win32_UI\"],\"Win32_UI_Wpf\":[\"Win32_UI\"],\"Win32_Web\":[\"Win32\"],\"Win32_Web_InternetExplorer\":[\"Win32_Web\"],\"default\":[\"std\"],\"deprecated\":[],\"docs\":[],\"implement\":[],\"std\":[\"windows-core/std\"]}}", - "windows_0.61.3": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-collections\",\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"windows-core\",\"req\":\"^0.61.2\"},{\"default_features\":false,\"name\":\"windows-future\",\"req\":\"^0.2.1\"},{\"default_features\":false,\"name\":\"windows-link\",\"req\":\"^0.1.3\"},{\"default_features\":false,\"name\":\"windows-numerics\",\"req\":\"^0.2.0\"}],\"features\":{\"AI\":[\"Foundation\"],\"AI_MachineLearning\":[\"AI\"],\"ApplicationModel\":[\"Foundation\"],\"ApplicationModel_Activation\":[\"ApplicationModel\"],\"ApplicationModel_AppExtensions\":[\"ApplicationModel\"],\"ApplicationModel_AppService\":[\"ApplicationModel\"],\"ApplicationModel_Appointments\":[\"ApplicationModel\"],\"ApplicationModel_Appointments_AppointmentsProvider\":[\"ApplicationModel_Appointments\"],\"ApplicationModel_Appointments_DataProvider\":[\"ApplicationModel_Appointments\"],\"ApplicationModel_Background\":[\"ApplicationModel\"],\"ApplicationModel_Calls\":[\"ApplicationModel\"],\"ApplicationModel_Calls_Background\":[\"ApplicationModel_Calls\"],\"ApplicationModel_Calls_Provider\":[\"ApplicationModel_Calls\"],\"ApplicationModel_Chat\":[\"ApplicationModel\"],\"ApplicationModel_CommunicationBlocking\":[\"ApplicationModel\"],\"ApplicationModel_Contacts\":[\"ApplicationModel\"],\"ApplicationModel_Contacts_DataProvider\":[\"ApplicationModel_Contacts\"],\"ApplicationModel_Contacts_Provider\":[\"ApplicationModel_Contacts\"],\"ApplicationModel_ConversationalAgent\":[\"ApplicationModel\"],\"ApplicationModel_Core\":[\"ApplicationModel\"],\"ApplicationModel_DataTransfer\":[\"ApplicationModel\"],\"ApplicationModel_DataTransfer_DragDrop\":[\"ApplicationModel_DataTransfer\"],\"ApplicationModel_DataTransfer_DragDrop_Core\":[\"ApplicationModel_DataTransfer_DragDrop\"],\"ApplicationModel_DataTransfer_ShareTarget\":[\"ApplicationModel_DataTransfer\"],\"ApplicationModel_Email\":[\"ApplicationModel\"],\"ApplicationModel_Email_DataProvider\":[\"ApplicationModel_Email\"],\"ApplicationModel_ExtendedExecution\":[\"ApplicationModel\"],\"ApplicationModel_ExtendedExecution_Foreground\":[\"ApplicationModel_ExtendedExecution\"],\"ApplicationModel_Holographic\":[\"ApplicationModel\"],\"ApplicationModel_LockScreen\":[\"ApplicationModel\"],\"ApplicationModel_PackageExtensions\":[\"ApplicationModel\"],\"ApplicationModel_Payments\":[\"ApplicationModel\"],\"ApplicationModel_Payments_Provider\":[\"ApplicationModel_Payments\"],\"ApplicationModel_Preview\":[\"ApplicationModel\"],\"ApplicationModel_Preview_Holographic\":[\"ApplicationModel_Preview\"],\"ApplicationModel_Preview_InkWorkspace\":[\"ApplicationModel_Preview\"],\"ApplicationModel_Preview_Notes\":[\"ApplicationModel_Preview\"],\"ApplicationModel_Resources\":[\"ApplicationModel\"],\"ApplicationModel_Resources_Core\":[\"ApplicationModel_Resources\"],\"ApplicationModel_Resources_Management\":[\"ApplicationModel_Resources\"],\"ApplicationModel_Search\":[\"ApplicationModel\"],\"ApplicationModel_Search_Core\":[\"ApplicationModel_Search\"],\"ApplicationModel_UserActivities\":[\"ApplicationModel\"],\"ApplicationModel_UserActivities_Core\":[\"ApplicationModel_UserActivities\"],\"ApplicationModel_UserDataAccounts\":[\"ApplicationModel\"],\"ApplicationModel_UserDataAccounts_Provider\":[\"ApplicationModel_UserDataAccounts\"],\"ApplicationModel_UserDataAccounts_SystemAccess\":[\"ApplicationModel_UserDataAccounts\"],\"ApplicationModel_UserDataTasks\":[\"ApplicationModel\"],\"ApplicationModel_UserDataTasks_DataProvider\":[\"ApplicationModel_UserDataTasks\"],\"ApplicationModel_VoiceCommands\":[\"ApplicationModel\"],\"ApplicationModel_Wallet\":[\"ApplicationModel\"],\"ApplicationModel_Wallet_System\":[\"ApplicationModel_Wallet\"],\"Data\":[\"Foundation\"],\"Data_Html\":[\"Data\"],\"Data_Json\":[\"Data\"],\"Data_Pdf\":[\"Data\"],\"Data_Text\":[\"Data\"],\"Data_Xml\":[\"Data\"],\"Data_Xml_Dom\":[\"Data_Xml\"],\"Data_Xml_Xsl\":[\"Data_Xml\"],\"Devices\":[\"Foundation\"],\"Devices_Adc\":[\"Devices\"],\"Devices_Adc_Provider\":[\"Devices_Adc\"],\"Devices_Background\":[\"Devices\"],\"Devices_Bluetooth\":[\"Devices\"],\"Devices_Bluetooth_Advertisement\":[\"Devices_Bluetooth\"],\"Devices_Bluetooth_Background\":[\"Devices_Bluetooth\"],\"Devices_Bluetooth_GenericAttributeProfile\":[\"Devices_Bluetooth\"],\"Devices_Bluetooth_Rfcomm\":[\"Devices_Bluetooth\"],\"Devices_Custom\":[\"Devices\"],\"Devices_Display\":[\"Devices\"],\"Devices_Display_Core\":[\"Devices_Display\"],\"Devices_Enumeration\":[\"Devices\"],\"Devices_Enumeration_Pnp\":[\"Devices_Enumeration\"],\"Devices_Geolocation\":[\"Devices\"],\"Devices_Geolocation_Geofencing\":[\"Devices_Geolocation\"],\"Devices_Geolocation_Provider\":[\"Devices_Geolocation\"],\"Devices_Gpio\":[\"Devices\"],\"Devices_Gpio_Provider\":[\"Devices_Gpio\"],\"Devices_Haptics\":[\"Devices\"],\"Devices_HumanInterfaceDevice\":[\"Devices\"],\"Devices_I2c\":[\"Devices\"],\"Devices_I2c_Provider\":[\"Devices_I2c\"],\"Devices_Input\":[\"Devices\"],\"Devices_Input_Preview\":[\"Devices_Input\"],\"Devices_Lights\":[\"Devices\"],\"Devices_Lights_Effects\":[\"Devices_Lights\"],\"Devices_Midi\":[\"Devices\"],\"Devices_PointOfService\":[\"Devices\"],\"Devices_PointOfService_Provider\":[\"Devices_PointOfService\"],\"Devices_Portable\":[\"Devices\"],\"Devices_Power\":[\"Devices\"],\"Devices_Printers\":[\"Devices\"],\"Devices_Printers_Extensions\":[\"Devices_Printers\"],\"Devices_Pwm\":[\"Devices\"],\"Devices_Pwm_Provider\":[\"Devices_Pwm\"],\"Devices_Radios\":[\"Devices\"],\"Devices_Scanners\":[\"Devices\"],\"Devices_Sensors\":[\"Devices\"],\"Devices_Sensors_Custom\":[\"Devices_Sensors\"],\"Devices_SerialCommunication\":[\"Devices\"],\"Devices_SmartCards\":[\"Devices\"],\"Devices_Sms\":[\"Devices\"],\"Devices_Spi\":[\"Devices\"],\"Devices_Spi_Provider\":[\"Devices_Spi\"],\"Devices_Usb\":[\"Devices\"],\"Devices_WiFi\":[\"Devices\"],\"Devices_WiFiDirect\":[\"Devices\"],\"Devices_WiFiDirect_Services\":[\"Devices_WiFiDirect\"],\"Embedded\":[\"Foundation\"],\"Embedded_DeviceLockdown\":[\"Embedded\"],\"Foundation\":[],\"Foundation_Collections\":[\"Foundation\"],\"Foundation_Diagnostics\":[\"Foundation\"],\"Foundation_Metadata\":[\"Foundation\"],\"Foundation_Numerics\":[\"Foundation\"],\"Gaming\":[\"Foundation\"],\"Gaming_Input\":[\"Gaming\"],\"Gaming_Input_Custom\":[\"Gaming_Input\"],\"Gaming_Input_ForceFeedback\":[\"Gaming_Input\"],\"Gaming_Input_Preview\":[\"Gaming_Input\"],\"Gaming_Preview\":[\"Gaming\"],\"Gaming_Preview_GamesEnumeration\":[\"Gaming_Preview\"],\"Gaming_UI\":[\"Gaming\"],\"Gaming_XboxLive\":[\"Gaming\"],\"Gaming_XboxLive_Storage\":[\"Gaming_XboxLive\"],\"Globalization\":[\"Foundation\"],\"Globalization_Collation\":[\"Globalization\"],\"Globalization_DateTimeFormatting\":[\"Globalization\"],\"Globalization_Fonts\":[\"Globalization\"],\"Globalization_NumberFormatting\":[\"Globalization\"],\"Globalization_PhoneNumberFormatting\":[\"Globalization\"],\"Graphics\":[\"Foundation\"],\"Graphics_Capture\":[\"Graphics\"],\"Graphics_DirectX\":[\"Graphics\"],\"Graphics_DirectX_Direct3D11\":[\"Graphics_DirectX\"],\"Graphics_Display\":[\"Graphics\"],\"Graphics_Display_Core\":[\"Graphics_Display\"],\"Graphics_Effects\":[\"Graphics\"],\"Graphics_Holographic\":[\"Graphics\"],\"Graphics_Imaging\":[\"Graphics\"],\"Graphics_Printing\":[\"Graphics\"],\"Graphics_Printing3D\":[\"Graphics\"],\"Graphics_Printing_OptionDetails\":[\"Graphics_Printing\"],\"Graphics_Printing_PrintSupport\":[\"Graphics_Printing\"],\"Graphics_Printing_PrintTicket\":[\"Graphics_Printing\"],\"Graphics_Printing_Workflow\":[\"Graphics_Printing\"],\"Management\":[\"Foundation\"],\"Management_Core\":[\"Management\"],\"Management_Deployment\":[\"Management\"],\"Management_Deployment_Preview\":[\"Management_Deployment\"],\"Management_Policies\":[\"Management\"],\"Management_Setup\":[\"Management\"],\"Management_Update\":[\"Management\"],\"Management_Workplace\":[\"Management\"],\"Media\":[\"Foundation\"],\"Media_AppBroadcasting\":[\"Media\"],\"Media_AppRecording\":[\"Media\"],\"Media_Audio\":[\"Media\"],\"Media_Capture\":[\"Media\"],\"Media_Capture_Core\":[\"Media_Capture\"],\"Media_Capture_Frames\":[\"Media_Capture\"],\"Media_Casting\":[\"Media\"],\"Media_ClosedCaptioning\":[\"Media\"],\"Media_ContentRestrictions\":[\"Media\"],\"Media_Control\":[\"Media\"],\"Media_Core\":[\"Media\"],\"Media_Core_Preview\":[\"Media_Core\"],\"Media_Devices\":[\"Media\"],\"Media_Devices_Core\":[\"Media_Devices\"],\"Media_DialProtocol\":[\"Media\"],\"Media_Editing\":[\"Media\"],\"Media_Effects\":[\"Media\"],\"Media_FaceAnalysis\":[\"Media\"],\"Media_Import\":[\"Media\"],\"Media_MediaProperties\":[\"Media\"],\"Media_Miracast\":[\"Media\"],\"Media_Ocr\":[\"Media\"],\"Media_PlayTo\":[\"Media\"],\"Media_Playback\":[\"Media\"],\"Media_Playlists\":[\"Media\"],\"Media_Protection\":[\"Media\"],\"Media_Protection_PlayReady\":[\"Media_Protection\"],\"Media_Render\":[\"Media\"],\"Media_SpeechRecognition\":[\"Media\"],\"Media_SpeechSynthesis\":[\"Media\"],\"Media_Streaming\":[\"Media\"],\"Media_Streaming_Adaptive\":[\"Media_Streaming\"],\"Media_Transcoding\":[\"Media\"],\"Networking\":[\"Foundation\"],\"Networking_BackgroundTransfer\":[\"Networking\"],\"Networking_Connectivity\":[\"Networking\"],\"Networking_NetworkOperators\":[\"Networking\"],\"Networking_Proximity\":[\"Networking\"],\"Networking_PushNotifications\":[\"Networking\"],\"Networking_ServiceDiscovery\":[\"Networking\"],\"Networking_ServiceDiscovery_Dnssd\":[\"Networking_ServiceDiscovery\"],\"Networking_Sockets\":[\"Networking\"],\"Networking_Vpn\":[\"Networking\"],\"Networking_XboxLive\":[\"Networking\"],\"Perception\":[\"Foundation\"],\"Perception_Automation\":[\"Perception\"],\"Perception_Automation_Core\":[\"Perception_Automation\"],\"Perception_People\":[\"Perception\"],\"Perception_Spatial\":[\"Perception\"],\"Perception_Spatial_Preview\":[\"Perception_Spatial\"],\"Perception_Spatial_Surfaces\":[\"Perception_Spatial\"],\"Phone\":[\"Foundation\"],\"Phone_ApplicationModel\":[\"Phone\"],\"Phone_Devices\":[\"Phone\"],\"Phone_Devices_Notification\":[\"Phone_Devices\"],\"Phone_Devices_Power\":[\"Phone_Devices\"],\"Phone_Management\":[\"Phone\"],\"Phone_Management_Deployment\":[\"Phone_Management\"],\"Phone_Media\":[\"Phone\"],\"Phone_Media_Devices\":[\"Phone_Media\"],\"Phone_Notification\":[\"Phone\"],\"Phone_Notification_Management\":[\"Phone_Notification\"],\"Phone_PersonalInformation\":[\"Phone\"],\"Phone_PersonalInformation_Provisioning\":[\"Phone_PersonalInformation\"],\"Phone_Speech\":[\"Phone\"],\"Phone_Speech_Recognition\":[\"Phone_Speech\"],\"Phone_StartScreen\":[\"Phone\"],\"Phone_System\":[\"Phone\"],\"Phone_System_Power\":[\"Phone_System\"],\"Phone_System_Profile\":[\"Phone_System\"],\"Phone_System_UserProfile\":[\"Phone_System\"],\"Phone_System_UserProfile_GameServices\":[\"Phone_System_UserProfile\"],\"Phone_System_UserProfile_GameServices_Core\":[\"Phone_System_UserProfile_GameServices\"],\"Phone_UI\":[\"Phone\"],\"Phone_UI_Input\":[\"Phone_UI\"],\"Security\":[\"Foundation\"],\"Security_Authentication\":[\"Security\"],\"Security_Authentication_Identity\":[\"Security_Authentication\"],\"Security_Authentication_Identity_Core\":[\"Security_Authentication_Identity\"],\"Security_Authentication_OnlineId\":[\"Security_Authentication\"],\"Security_Authentication_Web\":[\"Security_Authentication\"],\"Security_Authentication_Web_Core\":[\"Security_Authentication_Web\"],\"Security_Authentication_Web_Provider\":[\"Security_Authentication_Web\"],\"Security_Authorization\":[\"Security\"],\"Security_Authorization_AppCapabilityAccess\":[\"Security_Authorization\"],\"Security_Credentials\":[\"Security\"],\"Security_Credentials_UI\":[\"Security_Credentials\"],\"Security_Cryptography\":[\"Security\"],\"Security_Cryptography_Certificates\":[\"Security_Cryptography\"],\"Security_Cryptography_Core\":[\"Security_Cryptography\"],\"Security_Cryptography_DataProtection\":[\"Security_Cryptography\"],\"Security_DataProtection\":[\"Security\"],\"Security_EnterpriseData\":[\"Security\"],\"Security_ExchangeActiveSyncProvisioning\":[\"Security\"],\"Security_Isolation\":[\"Security\"],\"Services\":[\"Foundation\"],\"Services_Maps\":[\"Services\"],\"Services_Maps_Guidance\":[\"Services_Maps\"],\"Services_Maps_LocalSearch\":[\"Services_Maps\"],\"Services_Maps_OfflineMaps\":[\"Services_Maps\"],\"Services_Store\":[\"Services\"],\"Services_TargetedContent\":[\"Services\"],\"Storage\":[\"Foundation\"],\"Storage_AccessCache\":[\"Storage\"],\"Storage_BulkAccess\":[\"Storage\"],\"Storage_Compression\":[\"Storage\"],\"Storage_FileProperties\":[\"Storage\"],\"Storage_Pickers\":[\"Storage\"],\"Storage_Pickers_Provider\":[\"Storage_Pickers\"],\"Storage_Provider\":[\"Storage\"],\"Storage_Search\":[\"Storage\"],\"Storage_Streams\":[\"Storage\"],\"System\":[\"Foundation\"],\"System_Diagnostics\":[\"System\"],\"System_Diagnostics_DevicePortal\":[\"System_Diagnostics\"],\"System_Diagnostics_Telemetry\":[\"System_Diagnostics\"],\"System_Diagnostics_TraceReporting\":[\"System_Diagnostics\"],\"System_Display\":[\"System\"],\"System_Implementation\":[\"System\"],\"System_Implementation_FileExplorer\":[\"System_Implementation\"],\"System_Inventory\":[\"System\"],\"System_Power\":[\"System\"],\"System_Profile\":[\"System\"],\"System_Profile_SystemManufacturers\":[\"System_Profile\"],\"System_RemoteDesktop\":[\"System\"],\"System_RemoteDesktop_Input\":[\"System_RemoteDesktop\"],\"System_RemoteDesktop_Provider\":[\"System_RemoteDesktop\"],\"System_RemoteSystems\":[\"System\"],\"System_Threading\":[\"System\"],\"System_Threading_Core\":[\"System_Threading\"],\"System_Update\":[\"System\"],\"System_UserProfile\":[\"System\"],\"UI\":[\"Foundation\"],\"UI_Accessibility\":[\"UI\"],\"UI_ApplicationSettings\":[\"UI\"],\"UI_Composition\":[\"UI\"],\"UI_Composition_Core\":[\"UI_Composition\"],\"UI_Composition_Desktop\":[\"UI_Composition\"],\"UI_Composition_Diagnostics\":[\"UI_Composition\"],\"UI_Composition_Effects\":[\"UI_Composition\"],\"UI_Composition_Interactions\":[\"UI_Composition\"],\"UI_Composition_Scenes\":[\"UI_Composition\"],\"UI_Core\":[\"UI\"],\"UI_Core_AnimationMetrics\":[\"UI_Core\"],\"UI_Core_Preview\":[\"UI_Core\"],\"UI_Input\":[\"UI\"],\"UI_Input_Core\":[\"UI_Input\"],\"UI_Input_Inking\":[\"UI_Input\"],\"UI_Input_Inking_Analysis\":[\"UI_Input_Inking\"],\"UI_Input_Inking_Core\":[\"UI_Input_Inking\"],\"UI_Input_Inking_Preview\":[\"UI_Input_Inking\"],\"UI_Input_Preview\":[\"UI_Input\"],\"UI_Input_Preview_Injection\":[\"UI_Input_Preview\"],\"UI_Input_Spatial\":[\"UI_Input\"],\"UI_Notifications\":[\"UI\"],\"UI_Notifications_Management\":[\"UI_Notifications\"],\"UI_Notifications_Preview\":[\"UI_Notifications\"],\"UI_Popups\":[\"UI\"],\"UI_Shell\":[\"UI\"],\"UI_StartScreen\":[\"UI\"],\"UI_Text\":[\"UI\"],\"UI_Text_Core\":[\"UI_Text\"],\"UI_UIAutomation\":[\"UI\"],\"UI_UIAutomation_Core\":[\"UI_UIAutomation\"],\"UI_ViewManagement\":[\"UI\"],\"UI_ViewManagement_Core\":[\"UI_ViewManagement\"],\"UI_WebUI\":[\"UI\"],\"UI_WebUI_Core\":[\"UI_WebUI\"],\"UI_WindowManagement\":[\"UI\"],\"UI_WindowManagement_Preview\":[\"UI_WindowManagement\"],\"Wdk\":[\"Win32_Foundation\"],\"Wdk_Devices\":[\"Wdk\"],\"Wdk_Devices_Bluetooth\":[\"Wdk_Devices\"],\"Wdk_Devices_HumanInterfaceDevice\":[\"Wdk_Devices\"],\"Wdk_Foundation\":[\"Wdk\"],\"Wdk_Graphics\":[\"Wdk\"],\"Wdk_Graphics_Direct3D\":[\"Wdk_Graphics\"],\"Wdk_NetworkManagement\":[\"Wdk\"],\"Wdk_NetworkManagement_Ndis\":[\"Wdk_NetworkManagement\"],\"Wdk_NetworkManagement_WindowsFilteringPlatform\":[\"Wdk_NetworkManagement\"],\"Wdk_Storage\":[\"Wdk\"],\"Wdk_Storage_FileSystem\":[\"Wdk_Storage\"],\"Wdk_Storage_FileSystem_Minifilters\":[\"Wdk_Storage_FileSystem\"],\"Wdk_System\":[\"Wdk\"],\"Wdk_System_IO\":[\"Wdk_System\"],\"Wdk_System_Memory\":[\"Wdk_System\"],\"Wdk_System_OfflineRegistry\":[\"Wdk_System\"],\"Wdk_System_Registry\":[\"Wdk_System\"],\"Wdk_System_SystemInformation\":[\"Wdk_System\"],\"Wdk_System_SystemServices\":[\"Wdk_System\"],\"Wdk_System_Threading\":[\"Wdk_System\"],\"Web\":[\"Foundation\"],\"Web_AtomPub\":[\"Web\"],\"Web_Http\":[\"Web\"],\"Web_Http_Diagnostics\":[\"Web_Http\"],\"Web_Http_Filters\":[\"Web_Http\"],\"Web_Http_Headers\":[\"Web_Http\"],\"Web_Syndication\":[\"Web\"],\"Web_UI\":[\"Web\"],\"Web_UI_Interop\":[\"Web_UI\"],\"Win32\":[\"Win32_Foundation\"],\"Win32_AI\":[\"Win32\"],\"Win32_AI_MachineLearning\":[\"Win32_AI\"],\"Win32_AI_MachineLearning_DirectML\":[\"Win32_AI_MachineLearning\"],\"Win32_AI_MachineLearning_WinML\":[\"Win32_AI_MachineLearning\"],\"Win32_Data\":[\"Win32\"],\"Win32_Data_HtmlHelp\":[\"Win32_Data\"],\"Win32_Data_RightsManagement\":[\"Win32_Data\"],\"Win32_Data_Xml\":[\"Win32_Data\"],\"Win32_Data_Xml_MsXml\":[\"Win32_Data_Xml\"],\"Win32_Data_Xml_XmlLite\":[\"Win32_Data_Xml\"],\"Win32_Devices\":[\"Win32\"],\"Win32_Devices_AllJoyn\":[\"Win32_Devices\"],\"Win32_Devices_Beep\":[\"Win32_Devices\"],\"Win32_Devices_BiometricFramework\":[\"Win32_Devices\"],\"Win32_Devices_Bluetooth\":[\"Win32_Devices\"],\"Win32_Devices_Cdrom\":[\"Win32_Devices\"],\"Win32_Devices_Communication\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAccess\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAndDriverInstallation\":[\"Win32_Devices\"],\"Win32_Devices_DeviceQuery\":[\"Win32_Devices\"],\"Win32_Devices_Display\":[\"Win32_Devices\"],\"Win32_Devices_Dvd\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration_Pnp\":[\"Win32_Devices_Enumeration\"],\"Win32_Devices_Fax\":[\"Win32_Devices\"],\"Win32_Devices_FunctionDiscovery\":[\"Win32_Devices\"],\"Win32_Devices_Geolocation\":[\"Win32_Devices\"],\"Win32_Devices_HumanInterfaceDevice\":[\"Win32_Devices\"],\"Win32_Devices_ImageAcquisition\":[\"Win32_Devices\"],\"Win32_Devices_Nfc\":[\"Win32_Devices\"],\"Win32_Devices_Nfp\":[\"Win32_Devices\"],\"Win32_Devices_PortableDevices\":[\"Win32_Devices\"],\"Win32_Devices_Properties\":[\"Win32_Devices\"],\"Win32_Devices_Pwm\":[\"Win32_Devices\"],\"Win32_Devices_Sensors\":[\"Win32_Devices\"],\"Win32_Devices_SerialCommunication\":[\"Win32_Devices\"],\"Win32_Devices_Tapi\":[\"Win32_Devices\"],\"Win32_Devices_Usb\":[\"Win32_Devices\"],\"Win32_Devices_WebServicesOnDevices\":[\"Win32_Devices\"],\"Win32_Foundation\":[\"Win32\"],\"Win32_Gaming\":[\"Win32\"],\"Win32_Globalization\":[\"Win32\"],\"Win32_Graphics\":[\"Win32\"],\"Win32_Graphics_CompositionSwapchain\":[\"Win32_Graphics\"],\"Win32_Graphics_DXCore\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct2D\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct2D_Common\":[\"Win32_Graphics_Direct2D\"],\"Win32_Graphics_Direct3D\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D10\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D11\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D11on12\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D12\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D9\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D9on12\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D_Dxc\":[\"Win32_Graphics_Direct3D\"],\"Win32_Graphics_Direct3D_Fxc\":[\"Win32_Graphics_Direct3D\"],\"Win32_Graphics_DirectComposition\":[\"Win32_Graphics\"],\"Win32_Graphics_DirectDraw\":[\"Win32_Graphics\"],\"Win32_Graphics_DirectManipulation\":[\"Win32_Graphics\"],\"Win32_Graphics_DirectWrite\":[\"Win32_Graphics\"],\"Win32_Graphics_Dwm\":[\"Win32_Graphics\"],\"Win32_Graphics_Dxgi\":[\"Win32_Graphics\"],\"Win32_Graphics_Dxgi_Common\":[\"Win32_Graphics_Dxgi\"],\"Win32_Graphics_Gdi\":[\"Win32_Graphics\"],\"Win32_Graphics_GdiPlus\":[\"Win32_Graphics\"],\"Win32_Graphics_Hlsl\":[\"Win32_Graphics\"],\"Win32_Graphics_Imaging\":[\"Win32_Graphics\"],\"Win32_Graphics_Imaging_D2D\":[\"Win32_Graphics_Imaging\"],\"Win32_Graphics_OpenGL\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing_PrintTicket\":[\"Win32_Graphics_Printing\"],\"Win32_Management\":[\"Win32\"],\"Win32_Management_MobileDeviceManagementRegistration\":[\"Win32_Management\"],\"Win32_Media\":[\"Win32\"],\"Win32_Media_Audio\":[\"Win32_Media\"],\"Win32_Media_Audio_Apo\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_DirectMusic\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_DirectSound\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_Endpoints\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_XAudio2\":[\"Win32_Media_Audio\"],\"Win32_Media_DeviceManager\":[\"Win32_Media\"],\"Win32_Media_DirectShow\":[\"Win32_Media\"],\"Win32_Media_DirectShow_Tv\":[\"Win32_Media_DirectShow\"],\"Win32_Media_DirectShow_Xml\":[\"Win32_Media_DirectShow\"],\"Win32_Media_DxMediaObjects\":[\"Win32_Media\"],\"Win32_Media_KernelStreaming\":[\"Win32_Media\"],\"Win32_Media_LibrarySharingServices\":[\"Win32_Media\"],\"Win32_Media_MediaFoundation\":[\"Win32_Media\"],\"Win32_Media_MediaPlayer\":[\"Win32_Media\"],\"Win32_Media_Multimedia\":[\"Win32_Media\"],\"Win32_Media_PictureAcquisition\":[\"Win32_Media\"],\"Win32_Media_Speech\":[\"Win32_Media\"],\"Win32_Media_Streaming\":[\"Win32_Media\"],\"Win32_Media_WindowsMediaFormat\":[\"Win32_Media\"],\"Win32_NetworkManagement\":[\"Win32\"],\"Win32_NetworkManagement_Dhcp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Dns\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_InternetConnectionWizard\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_IpHelper\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_MobileBroadband\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Multicast\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Ndis\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetBios\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetManagement\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetShell\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkDiagnosticsFramework\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkPolicyServer\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_P2P\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_QoS\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Rras\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Snmp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WNet\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WebDav\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WiFi\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectNow\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectionManager\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFilteringPlatform\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFirewall\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsNetworkVirtualization\":[\"Win32_NetworkManagement\"],\"Win32_Networking\":[\"Win32\"],\"Win32_Networking_ActiveDirectory\":[\"Win32_Networking\"],\"Win32_Networking_BackgroundIntelligentTransferService\":[\"Win32_Networking\"],\"Win32_Networking_Clustering\":[\"Win32_Networking\"],\"Win32_Networking_HttpServer\":[\"Win32_Networking\"],\"Win32_Networking_Ldap\":[\"Win32_Networking\"],\"Win32_Networking_NetworkListManager\":[\"Win32_Networking\"],\"Win32_Networking_RemoteDifferentialCompression\":[\"Win32_Networking\"],\"Win32_Networking_WebSocket\":[\"Win32_Networking\"],\"Win32_Networking_WinHttp\":[\"Win32_Networking\"],\"Win32_Networking_WinInet\":[\"Win32_Networking\"],\"Win32_Networking_WinSock\":[\"Win32_Networking\"],\"Win32_Networking_WindowsWebServices\":[\"Win32_Networking\"],\"Win32_Security\":[\"Win32\"],\"Win32_Security_AppLocker\":[\"Win32_Security\"],\"Win32_Security_Authentication\":[\"Win32_Security\"],\"Win32_Security_Authentication_Identity\":[\"Win32_Security_Authentication\"],\"Win32_Security_Authentication_Identity_Provider\":[\"Win32_Security_Authentication_Identity\"],\"Win32_Security_Authorization\":[\"Win32_Security\"],\"Win32_Security_Authorization_UI\":[\"Win32_Security_Authorization\"],\"Win32_Security_ConfigurationSnapin\":[\"Win32_Security\"],\"Win32_Security_Credentials\":[\"Win32_Security\"],\"Win32_Security_Cryptography\":[\"Win32_Security\"],\"Win32_Security_Cryptography_Catalog\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Certificates\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Sip\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_UI\":[\"Win32_Security_Cryptography\"],\"Win32_Security_DiagnosticDataQuery\":[\"Win32_Security\"],\"Win32_Security_DirectoryServices\":[\"Win32_Security\"],\"Win32_Security_EnterpriseData\":[\"Win32_Security\"],\"Win32_Security_ExtensibleAuthenticationProtocol\":[\"Win32_Security\"],\"Win32_Security_Isolation\":[\"Win32_Security\"],\"Win32_Security_LicenseProtection\":[\"Win32_Security\"],\"Win32_Security_NetworkAccessProtection\":[\"Win32_Security\"],\"Win32_Security_Tpm\":[\"Win32_Security\"],\"Win32_Security_WinTrust\":[\"Win32_Security\"],\"Win32_Security_WinWlx\":[\"Win32_Security\"],\"Win32_Storage\":[\"Win32\"],\"Win32_Storage_Cabinets\":[\"Win32_Storage\"],\"Win32_Storage_CloudFilters\":[\"Win32_Storage\"],\"Win32_Storage_Compression\":[\"Win32_Storage\"],\"Win32_Storage_DataDeduplication\":[\"Win32_Storage\"],\"Win32_Storage_DistributedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_EnhancedStorage\":[\"Win32_Storage\"],\"Win32_Storage_FileHistory\":[\"Win32_Storage\"],\"Win32_Storage_FileServerResourceManager\":[\"Win32_Storage\"],\"Win32_Storage_FileSystem\":[\"Win32_Storage\"],\"Win32_Storage_Imapi\":[\"Win32_Storage\"],\"Win32_Storage_IndexServer\":[\"Win32_Storage\"],\"Win32_Storage_InstallableFileSystems\":[\"Win32_Storage\"],\"Win32_Storage_IscsiDisc\":[\"Win32_Storage\"],\"Win32_Storage_Jet\":[\"Win32_Storage\"],\"Win32_Storage_Nvme\":[\"Win32_Storage\"],\"Win32_Storage_OfflineFiles\":[\"Win32_Storage\"],\"Win32_Storage_OperationRecorder\":[\"Win32_Storage\"],\"Win32_Storage_Packaging\":[\"Win32_Storage\"],\"Win32_Storage_Packaging_Appx\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_Packaging_Opc\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_ProjectedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_StructuredStorage\":[\"Win32_Storage\"],\"Win32_Storage_Vhd\":[\"Win32_Storage\"],\"Win32_Storage_VirtualDiskService\":[\"Win32_Storage\"],\"Win32_Storage_Vss\":[\"Win32_Storage\"],\"Win32_Storage_Xps\":[\"Win32_Storage\"],\"Win32_Storage_Xps_Printing\":[\"Win32_Storage_Xps\"],\"Win32_System\":[\"Win32\"],\"Win32_System_AddressBook\":[\"Win32_System\"],\"Win32_System_Antimalware\":[\"Win32_System\"],\"Win32_System_ApplicationInstallationAndServicing\":[\"Win32_System\"],\"Win32_System_ApplicationVerifier\":[\"Win32_System\"],\"Win32_System_AssessmentTool\":[\"Win32_System\"],\"Win32_System_ClrHosting\":[\"Win32_System\"],\"Win32_System_Com\":[\"Win32_System\"],\"Win32_System_Com_CallObj\":[\"Win32_System_Com\"],\"Win32_System_Com_ChannelCredentials\":[\"Win32_System_Com\"],\"Win32_System_Com_Events\":[\"Win32_System_Com\"],\"Win32_System_Com_Marshal\":[\"Win32_System_Com\"],\"Win32_System_Com_StructuredStorage\":[\"Win32_System_Com\"],\"Win32_System_Com_UI\":[\"Win32_System_Com\"],\"Win32_System_Com_Urlmon\":[\"Win32_System_Com\"],\"Win32_System_ComponentServices\":[\"Win32_System\"],\"Win32_System_Console\":[\"Win32_System\"],\"Win32_System_Contacts\":[\"Win32_System\"],\"Win32_System_CorrelationVector\":[\"Win32_System\"],\"Win32_System_DataExchange\":[\"Win32_System\"],\"Win32_System_DeploymentServices\":[\"Win32_System\"],\"Win32_System_DesktopSharing\":[\"Win32_System\"],\"Win32_System_DeveloperLicensing\":[\"Win32_System\"],\"Win32_System_Diagnostics\":[\"Win32_System\"],\"Win32_System_Diagnostics_Ceip\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ClrProfiling\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug_ActiveScript\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Debug_Extensions\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Etw\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ProcessSnapshotting\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ToolHelp\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_TraceLogging\":[\"Win32_System_Diagnostics\"],\"Win32_System_DistributedTransactionCoordinator\":[\"Win32_System\"],\"Win32_System_Environment\":[\"Win32_System\"],\"Win32_System_ErrorReporting\":[\"Win32_System\"],\"Win32_System_EventCollector\":[\"Win32_System\"],\"Win32_System_EventLog\":[\"Win32_System\"],\"Win32_System_EventNotificationService\":[\"Win32_System\"],\"Win32_System_GroupPolicy\":[\"Win32_System\"],\"Win32_System_HostCompute\":[\"Win32_System\"],\"Win32_System_HostComputeNetwork\":[\"Win32_System\"],\"Win32_System_HostComputeSystem\":[\"Win32_System\"],\"Win32_System_Hypervisor\":[\"Win32_System\"],\"Win32_System_IO\":[\"Win32_System\"],\"Win32_System_Iis\":[\"Win32_System\"],\"Win32_System_Ioctl\":[\"Win32_System\"],\"Win32_System_JobObjects\":[\"Win32_System\"],\"Win32_System_Js\":[\"Win32_System\"],\"Win32_System_Kernel\":[\"Win32_System\"],\"Win32_System_LibraryLoader\":[\"Win32_System\"],\"Win32_System_Mailslots\":[\"Win32_System\"],\"Win32_System_Mapi\":[\"Win32_System\"],\"Win32_System_Memory\":[\"Win32_System\"],\"Win32_System_Memory_NonVolatile\":[\"Win32_System_Memory\"],\"Win32_System_MessageQueuing\":[\"Win32_System\"],\"Win32_System_MixedReality\":[\"Win32_System\"],\"Win32_System_Mmc\":[\"Win32_System\"],\"Win32_System_Ole\":[\"Win32_System\"],\"Win32_System_ParentalControls\":[\"Win32_System\"],\"Win32_System_PasswordManagement\":[\"Win32_System\"],\"Win32_System_Performance\":[\"Win32_System\"],\"Win32_System_Performance_HardwareCounterProfiling\":[\"Win32_System_Performance\"],\"Win32_System_Pipes\":[\"Win32_System\"],\"Win32_System_Power\":[\"Win32_System\"],\"Win32_System_ProcessStatus\":[\"Win32_System\"],\"Win32_System_RealTimeCommunications\":[\"Win32_System\"],\"Win32_System_Recovery\":[\"Win32_System\"],\"Win32_System_Registry\":[\"Win32_System\"],\"Win32_System_RemoteAssistance\":[\"Win32_System\"],\"Win32_System_RemoteDesktop\":[\"Win32_System\"],\"Win32_System_RemoteManagement\":[\"Win32_System\"],\"Win32_System_RestartManager\":[\"Win32_System\"],\"Win32_System_Restore\":[\"Win32_System\"],\"Win32_System_Rpc\":[\"Win32_System\"],\"Win32_System_Search\":[\"Win32_System\"],\"Win32_System_Search_Common\":[\"Win32_System_Search\"],\"Win32_System_SecurityCenter\":[\"Win32_System\"],\"Win32_System_ServerBackup\":[\"Win32_System\"],\"Win32_System_Services\":[\"Win32_System\"],\"Win32_System_SettingsManagementInfrastructure\":[\"Win32_System\"],\"Win32_System_SetupAndMigration\":[\"Win32_System\"],\"Win32_System_Shutdown\":[\"Win32_System\"],\"Win32_System_SideShow\":[\"Win32_System\"],\"Win32_System_StationsAndDesktops\":[\"Win32_System\"],\"Win32_System_SubsystemForLinux\":[\"Win32_System\"],\"Win32_System_SystemInformation\":[\"Win32_System\"],\"Win32_System_SystemServices\":[\"Win32_System\"],\"Win32_System_TaskScheduler\":[\"Win32_System\"],\"Win32_System_Threading\":[\"Win32_System\"],\"Win32_System_Time\":[\"Win32_System\"],\"Win32_System_TpmBaseServices\":[\"Win32_System\"],\"Win32_System_TransactionServer\":[\"Win32_System\"],\"Win32_System_UpdateAgent\":[\"Win32_System\"],\"Win32_System_UpdateAssessment\":[\"Win32_System\"],\"Win32_System_UserAccessLogging\":[\"Win32_System\"],\"Win32_System_Variant\":[\"Win32_System\"],\"Win32_System_VirtualDosMachines\":[\"Win32_System\"],\"Win32_System_WinRT\":[\"Win32_System\"],\"Win32_System_WinRT_AllJoyn\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Composition\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_CoreInputView\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Direct3D11\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Display\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Graphics\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Graphics_Capture\":[\"Win32_System_WinRT_Graphics\"],\"Win32_System_WinRT_Graphics_Direct2D\":[\"Win32_System_WinRT_Graphics\"],\"Win32_System_WinRT_Graphics_Imaging\":[\"Win32_System_WinRT_Graphics\"],\"Win32_System_WinRT_Holographic\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Isolation\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_ML\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Media\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Metadata\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Pdf\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Printing\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Shell\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Storage\":[\"Win32_System_WinRT\"],\"Win32_System_WindowsProgramming\":[\"Win32_System\"],\"Win32_System_WindowsSync\":[\"Win32_System\"],\"Win32_System_Wmi\":[\"Win32_System\"],\"Win32_UI\":[\"Win32\"],\"Win32_UI_Accessibility\":[\"Win32_UI\"],\"Win32_UI_Animation\":[\"Win32_UI\"],\"Win32_UI_ColorSystem\":[\"Win32_UI\"],\"Win32_UI_Controls\":[\"Win32_UI\"],\"Win32_UI_Controls_Dialogs\":[\"Win32_UI_Controls\"],\"Win32_UI_Controls_RichEdit\":[\"Win32_UI_Controls\"],\"Win32_UI_HiDpi\":[\"Win32_UI\"],\"Win32_UI_Input\":[\"Win32_UI\"],\"Win32_UI_Input_Ime\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Ink\":[\"Win32_UI_Input\"],\"Win32_UI_Input_KeyboardAndMouse\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Pointer\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Radial\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Touch\":[\"Win32_UI_Input\"],\"Win32_UI_Input_XboxController\":[\"Win32_UI_Input\"],\"Win32_UI_InteractionContext\":[\"Win32_UI\"],\"Win32_UI_LegacyWindowsEnvironmentFeatures\":[\"Win32_UI\"],\"Win32_UI_Magnification\":[\"Win32_UI\"],\"Win32_UI_Notifications\":[\"Win32_UI\"],\"Win32_UI_Ribbon\":[\"Win32_UI\"],\"Win32_UI_Shell\":[\"Win32_UI\"],\"Win32_UI_Shell_Common\":[\"Win32_UI_Shell\"],\"Win32_UI_Shell_PropertiesSystem\":[\"Win32_UI_Shell\"],\"Win32_UI_TabletPC\":[\"Win32_UI\"],\"Win32_UI_TextServices\":[\"Win32_UI\"],\"Win32_UI_WindowsAndMessaging\":[\"Win32_UI\"],\"Win32_UI_Wpf\":[\"Win32_UI\"],\"Win32_Web\":[\"Win32\"],\"Win32_Web_InternetExplorer\":[\"Win32_Web\"],\"default\":[\"std\"],\"deprecated\":[],\"docs\":[],\"std\":[\"windows-core/std\"]}}", + "windows_0.62.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"windows-collections\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"name\":\"windows-core\",\"req\":\"^0.62.2\"},{\"default_features\":false,\"name\":\"windows-future\",\"req\":\"^0.3.2\"},{\"default_features\":false,\"name\":\"windows-numerics\",\"req\":\"^0.3.1\"}],\"features\":{\"AI\":[\"Foundation\"],\"AI_Actions\":[\"AI\"],\"AI_Actions_Hosting\":[\"AI_Actions\"],\"AI_Actions_Provider\":[\"AI_Actions\"],\"AI_Agents\":[\"AI\"],\"AI_Agents_Mcp\":[\"AI_Agents\"],\"AI_MachineLearning\":[\"AI\"],\"ApplicationModel\":[\"Foundation\"],\"ApplicationModel_Activation\":[\"ApplicationModel\"],\"ApplicationModel_AppExtensions\":[\"ApplicationModel\"],\"ApplicationModel_AppService\":[\"ApplicationModel\"],\"ApplicationModel_Appointments\":[\"ApplicationModel\"],\"ApplicationModel_Appointments_AppointmentsProvider\":[\"ApplicationModel_Appointments\"],\"ApplicationModel_Appointments_DataProvider\":[\"ApplicationModel_Appointments\"],\"ApplicationModel_Background\":[\"ApplicationModel\"],\"ApplicationModel_Calls\":[\"ApplicationModel\"],\"ApplicationModel_Calls_Background\":[\"ApplicationModel_Calls\"],\"ApplicationModel_Calls_Provider\":[\"ApplicationModel_Calls\"],\"ApplicationModel_Chat\":[\"ApplicationModel\"],\"ApplicationModel_CommunicationBlocking\":[\"ApplicationModel\"],\"ApplicationModel_Contacts\":[\"ApplicationModel\"],\"ApplicationModel_Contacts_DataProvider\":[\"ApplicationModel_Contacts\"],\"ApplicationModel_Contacts_Provider\":[\"ApplicationModel_Contacts\"],\"ApplicationModel_ConversationalAgent\":[\"ApplicationModel\"],\"ApplicationModel_Core\":[\"ApplicationModel\"],\"ApplicationModel_DataTransfer\":[\"ApplicationModel\"],\"ApplicationModel_DataTransfer_DragDrop\":[\"ApplicationModel_DataTransfer\"],\"ApplicationModel_DataTransfer_DragDrop_Core\":[\"ApplicationModel_DataTransfer_DragDrop\"],\"ApplicationModel_DataTransfer_ShareTarget\":[\"ApplicationModel_DataTransfer\"],\"ApplicationModel_Email\":[\"ApplicationModel\"],\"ApplicationModel_Email_DataProvider\":[\"ApplicationModel_Email\"],\"ApplicationModel_ExtendedExecution\":[\"ApplicationModel\"],\"ApplicationModel_ExtendedExecution_Foreground\":[\"ApplicationModel_ExtendedExecution\"],\"ApplicationModel_Holographic\":[\"ApplicationModel\"],\"ApplicationModel_LockScreen\":[\"ApplicationModel\"],\"ApplicationModel_PackageExtensions\":[\"ApplicationModel\"],\"ApplicationModel_Payments\":[\"ApplicationModel\"],\"ApplicationModel_Payments_Provider\":[\"ApplicationModel_Payments\"],\"ApplicationModel_Preview\":[\"ApplicationModel\"],\"ApplicationModel_Preview_Holographic\":[\"ApplicationModel_Preview\"],\"ApplicationModel_Preview_InkWorkspace\":[\"ApplicationModel_Preview\"],\"ApplicationModel_Preview_Notes\":[\"ApplicationModel_Preview\"],\"ApplicationModel_Resources\":[\"ApplicationModel\"],\"ApplicationModel_Resources_Core\":[\"ApplicationModel_Resources\"],\"ApplicationModel_Resources_Management\":[\"ApplicationModel_Resources\"],\"ApplicationModel_Search\":[\"ApplicationModel\"],\"ApplicationModel_Search_Core\":[\"ApplicationModel_Search\"],\"ApplicationModel_UserActivities\":[\"ApplicationModel\"],\"ApplicationModel_UserActivities_Core\":[\"ApplicationModel_UserActivities\"],\"ApplicationModel_UserDataAccounts\":[\"ApplicationModel\"],\"ApplicationModel_UserDataAccounts_Provider\":[\"ApplicationModel_UserDataAccounts\"],\"ApplicationModel_UserDataAccounts_SystemAccess\":[\"ApplicationModel_UserDataAccounts\"],\"ApplicationModel_UserDataTasks\":[\"ApplicationModel\"],\"ApplicationModel_UserDataTasks_DataProvider\":[\"ApplicationModel_UserDataTasks\"],\"ApplicationModel_VoiceCommands\":[\"ApplicationModel\"],\"ApplicationModel_Wallet\":[\"ApplicationModel\"],\"ApplicationModel_Wallet_System\":[\"ApplicationModel_Wallet\"],\"Data\":[\"Foundation\"],\"Data_Html\":[\"Data\"],\"Data_Json\":[\"Data\"],\"Data_Pdf\":[\"Data\"],\"Data_Text\":[\"Data\"],\"Data_Xml\":[\"Data\"],\"Data_Xml_Dom\":[\"Data_Xml\"],\"Data_Xml_Xsl\":[\"Data_Xml\"],\"Devices\":[\"Foundation\"],\"Devices_Adc\":[\"Devices\"],\"Devices_Adc_Provider\":[\"Devices_Adc\"],\"Devices_Background\":[\"Devices\"],\"Devices_Bluetooth\":[\"Devices\"],\"Devices_Bluetooth_Advertisement\":[\"Devices_Bluetooth\"],\"Devices_Bluetooth_Background\":[\"Devices_Bluetooth\"],\"Devices_Bluetooth_GenericAttributeProfile\":[\"Devices_Bluetooth\"],\"Devices_Bluetooth_Rfcomm\":[\"Devices_Bluetooth\"],\"Devices_Custom\":[\"Devices\"],\"Devices_Display\":[\"Devices\"],\"Devices_Display_Core\":[\"Devices_Display\"],\"Devices_Enumeration\":[\"Devices\"],\"Devices_Enumeration_Pnp\":[\"Devices_Enumeration\"],\"Devices_Geolocation\":[\"Devices\"],\"Devices_Geolocation_Geofencing\":[\"Devices_Geolocation\"],\"Devices_Geolocation_Provider\":[\"Devices_Geolocation\"],\"Devices_Gpio\":[\"Devices\"],\"Devices_Gpio_Provider\":[\"Devices_Gpio\"],\"Devices_Haptics\":[\"Devices\"],\"Devices_HumanInterfaceDevice\":[\"Devices\"],\"Devices_I2c\":[\"Devices\"],\"Devices_I2c_Provider\":[\"Devices_I2c\"],\"Devices_Input\":[\"Devices\"],\"Devices_Input_Preview\":[\"Devices_Input\"],\"Devices_Lights\":[\"Devices\"],\"Devices_Lights_Effects\":[\"Devices_Lights\"],\"Devices_Midi\":[\"Devices\"],\"Devices_PointOfService\":[\"Devices\"],\"Devices_PointOfService_Provider\":[\"Devices_PointOfService\"],\"Devices_Portable\":[\"Devices\"],\"Devices_Power\":[\"Devices\"],\"Devices_Printers\":[\"Devices\"],\"Devices_Printers_Extensions\":[\"Devices_Printers\"],\"Devices_Pwm\":[\"Devices\"],\"Devices_Pwm_Provider\":[\"Devices_Pwm\"],\"Devices_Radios\":[\"Devices\"],\"Devices_Scanners\":[\"Devices\"],\"Devices_Sensors\":[\"Devices\"],\"Devices_Sensors_Custom\":[\"Devices_Sensors\"],\"Devices_SerialCommunication\":[\"Devices\"],\"Devices_SmartCards\":[\"Devices\"],\"Devices_Sms\":[\"Devices\"],\"Devices_Spi\":[\"Devices\"],\"Devices_Spi_Provider\":[\"Devices_Spi\"],\"Devices_Usb\":[\"Devices\"],\"Devices_WiFi\":[\"Devices\"],\"Devices_WiFiDirect\":[\"Devices\"],\"Devices_WiFiDirect_Services\":[\"Devices_WiFiDirect\"],\"Foundation\":[],\"Foundation_Collections\":[\"Foundation\"],\"Foundation_Diagnostics\":[\"Foundation\"],\"Foundation_Metadata\":[\"Foundation\"],\"Foundation_Numerics\":[\"Foundation\"],\"Gaming\":[\"Foundation\"],\"Gaming_Input\":[\"Gaming\"],\"Gaming_Input_Custom\":[\"Gaming_Input\"],\"Gaming_Input_ForceFeedback\":[\"Gaming_Input\"],\"Gaming_Input_Preview\":[\"Gaming_Input\"],\"Gaming_Preview\":[\"Gaming\"],\"Gaming_Preview_GamesEnumeration\":[\"Gaming_Preview\"],\"Gaming_UI\":[\"Gaming\"],\"Gaming_XboxLive\":[\"Gaming\"],\"Gaming_XboxLive_Storage\":[\"Gaming_XboxLive\"],\"Globalization\":[\"Foundation\"],\"Globalization_Collation\":[\"Globalization\"],\"Globalization_DateTimeFormatting\":[\"Globalization\"],\"Globalization_Fonts\":[\"Globalization\"],\"Globalization_NumberFormatting\":[\"Globalization\"],\"Globalization_PhoneNumberFormatting\":[\"Globalization\"],\"Graphics\":[\"Foundation\"],\"Graphics_Capture\":[\"Graphics\"],\"Graphics_DirectX\":[\"Graphics\"],\"Graphics_DirectX_Direct3D11\":[\"Graphics_DirectX\"],\"Graphics_Display\":[\"Graphics\"],\"Graphics_Display_Core\":[\"Graphics_Display\"],\"Graphics_Effects\":[\"Graphics\"],\"Graphics_Holographic\":[\"Graphics\"],\"Graphics_Imaging\":[\"Graphics\"],\"Graphics_Printing\":[\"Graphics\"],\"Graphics_Printing3D\":[\"Graphics\"],\"Graphics_Printing_OptionDetails\":[\"Graphics_Printing\"],\"Graphics_Printing_PrintSupport\":[\"Graphics_Printing\"],\"Graphics_Printing_PrintTicket\":[\"Graphics_Printing\"],\"Graphics_Printing_ProtectedPrint\":[\"Graphics_Printing\"],\"Graphics_Printing_Workflow\":[\"Graphics_Printing\"],\"Management\":[\"Foundation\"],\"Management_Core\":[\"Management\"],\"Management_Deployment\":[\"Management\"],\"Management_Deployment_Preview\":[\"Management_Deployment\"],\"Management_Policies\":[\"Management\"],\"Management_Setup\":[\"Management\"],\"Management_Update\":[\"Management\"],\"Management_Workplace\":[\"Management\"],\"Media\":[\"Foundation\"],\"Media_AppBroadcasting\":[\"Media\"],\"Media_AppRecording\":[\"Media\"],\"Media_Audio\":[\"Media\"],\"Media_Capture\":[\"Media\"],\"Media_Capture_Core\":[\"Media_Capture\"],\"Media_Capture_Frames\":[\"Media_Capture\"],\"Media_Casting\":[\"Media\"],\"Media_ClosedCaptioning\":[\"Media\"],\"Media_ContentRestrictions\":[\"Media\"],\"Media_Control\":[\"Media\"],\"Media_Core\":[\"Media\"],\"Media_Core_Preview\":[\"Media_Core\"],\"Media_Devices\":[\"Media\"],\"Media_Devices_Core\":[\"Media_Devices\"],\"Media_DialProtocol\":[\"Media\"],\"Media_Editing\":[\"Media\"],\"Media_Effects\":[\"Media\"],\"Media_FaceAnalysis\":[\"Media\"],\"Media_Import\":[\"Media\"],\"Media_MediaProperties\":[\"Media\"],\"Media_Miracast\":[\"Media\"],\"Media_Ocr\":[\"Media\"],\"Media_PlayTo\":[\"Media\"],\"Media_Playback\":[\"Media\"],\"Media_Playlists\":[\"Media\"],\"Media_Protection\":[\"Media\"],\"Media_Protection_PlayReady\":[\"Media_Protection\"],\"Media_Render\":[\"Media\"],\"Media_SpeechRecognition\":[\"Media\"],\"Media_SpeechSynthesis\":[\"Media\"],\"Media_Streaming\":[\"Media\"],\"Media_Streaming_Adaptive\":[\"Media_Streaming\"],\"Media_Transcoding\":[\"Media\"],\"Networking\":[\"Foundation\"],\"Networking_BackgroundTransfer\":[\"Networking\"],\"Networking_Connectivity\":[\"Networking\"],\"Networking_NetworkOperators\":[\"Networking\"],\"Networking_Proximity\":[\"Networking\"],\"Networking_PushNotifications\":[\"Networking\"],\"Networking_ServiceDiscovery\":[\"Networking\"],\"Networking_ServiceDiscovery_Dnssd\":[\"Networking_ServiceDiscovery\"],\"Networking_Sockets\":[\"Networking\"],\"Networking_Vpn\":[\"Networking\"],\"Networking_XboxLive\":[\"Networking\"],\"Perception\":[\"Foundation\"],\"Perception_Automation\":[\"Perception\"],\"Perception_Automation_Core\":[\"Perception_Automation\"],\"Perception_People\":[\"Perception\"],\"Perception_Spatial\":[\"Perception\"],\"Perception_Spatial_Preview\":[\"Perception_Spatial\"],\"Perception_Spatial_Surfaces\":[\"Perception_Spatial\"],\"Security\":[\"Foundation\"],\"Security_Authentication\":[\"Security\"],\"Security_Authentication_Identity\":[\"Security_Authentication\"],\"Security_Authentication_Identity_Core\":[\"Security_Authentication_Identity\"],\"Security_Authentication_OnlineId\":[\"Security_Authentication\"],\"Security_Authentication_Web\":[\"Security_Authentication\"],\"Security_Authentication_Web_Core\":[\"Security_Authentication_Web\"],\"Security_Authentication_Web_Provider\":[\"Security_Authentication_Web\"],\"Security_Authorization\":[\"Security\"],\"Security_Authorization_AppCapabilityAccess\":[\"Security_Authorization\"],\"Security_Credentials\":[\"Security\"],\"Security_Credentials_UI\":[\"Security_Credentials\"],\"Security_Cryptography\":[\"Security\"],\"Security_Cryptography_Certificates\":[\"Security_Cryptography\"],\"Security_Cryptography_Core\":[\"Security_Cryptography\"],\"Security_Cryptography_DataProtection\":[\"Security_Cryptography\"],\"Security_DataProtection\":[\"Security\"],\"Security_EnterpriseData\":[\"Security\"],\"Security_ExchangeActiveSyncProvisioning\":[\"Security\"],\"Security_Isolation\":[\"Security\"],\"Services\":[\"Foundation\"],\"Services_Maps\":[\"Services\"],\"Services_Maps_Guidance\":[\"Services_Maps\"],\"Services_Maps_LocalSearch\":[\"Services_Maps\"],\"Services_Maps_OfflineMaps\":[\"Services_Maps\"],\"Services_Store\":[\"Services\"],\"Services_TargetedContent\":[\"Services\"],\"Storage\":[\"Foundation\"],\"Storage_AccessCache\":[\"Storage\"],\"Storage_BulkAccess\":[\"Storage\"],\"Storage_Compression\":[\"Storage\"],\"Storage_FileProperties\":[\"Storage\"],\"Storage_Pickers\":[\"Storage\"],\"Storage_Pickers_Provider\":[\"Storage_Pickers\"],\"Storage_Provider\":[\"Storage\"],\"Storage_Search\":[\"Storage\"],\"Storage_Streams\":[\"Storage\"],\"System\":[\"Foundation\"],\"System_Diagnostics\":[\"System\"],\"System_Diagnostics_DevicePortal\":[\"System_Diagnostics\"],\"System_Diagnostics_Telemetry\":[\"System_Diagnostics\"],\"System_Diagnostics_TraceReporting\":[\"System_Diagnostics\"],\"System_Display\":[\"System\"],\"System_Implementation\":[\"System\"],\"System_Implementation_FileExplorer\":[\"System_Implementation\"],\"System_Inventory\":[\"System\"],\"System_Power\":[\"System\"],\"System_Profile\":[\"System\"],\"System_Profile_SystemManufacturers\":[\"System_Profile\"],\"System_RemoteDesktop\":[\"System\"],\"System_RemoteDesktop_Input\":[\"System_RemoteDesktop\"],\"System_RemoteDesktop_Provider\":[\"System_RemoteDesktop\"],\"System_RemoteSystems\":[\"System\"],\"System_Threading\":[\"System\"],\"System_Threading_Core\":[\"System_Threading\"],\"System_Update\":[\"System\"],\"System_UserProfile\":[\"System\"],\"UI\":[\"Foundation\"],\"UI_Accessibility\":[\"UI\"],\"UI_ApplicationSettings\":[\"UI\"],\"UI_Composition\":[\"UI\"],\"UI_Composition_Core\":[\"UI_Composition\"],\"UI_Composition_Desktop\":[\"UI_Composition\"],\"UI_Composition_Diagnostics\":[\"UI_Composition\"],\"UI_Composition_Effects\":[\"UI_Composition\"],\"UI_Composition_Interactions\":[\"UI_Composition\"],\"UI_Composition_Scenes\":[\"UI_Composition\"],\"UI_Core\":[\"UI\"],\"UI_Core_AnimationMetrics\":[\"UI_Core\"],\"UI_Core_Preview\":[\"UI_Core\"],\"UI_Input\":[\"UI\"],\"UI_Input_Core\":[\"UI_Input\"],\"UI_Input_Inking\":[\"UI_Input\"],\"UI_Input_Inking_Analysis\":[\"UI_Input_Inking\"],\"UI_Input_Inking_Core\":[\"UI_Input_Inking\"],\"UI_Input_Inking_Preview\":[\"UI_Input_Inking\"],\"UI_Input_Preview\":[\"UI_Input\"],\"UI_Input_Preview_Injection\":[\"UI_Input_Preview\"],\"UI_Input_Preview_Text\":[\"UI_Input_Preview\"],\"UI_Input_Spatial\":[\"UI_Input\"],\"UI_Notifications\":[\"UI\"],\"UI_Notifications_Management\":[\"UI_Notifications\"],\"UI_Notifications_Preview\":[\"UI_Notifications\"],\"UI_Popups\":[\"UI\"],\"UI_Shell\":[\"UI\"],\"UI_StartScreen\":[\"UI\"],\"UI_Text\":[\"UI\"],\"UI_Text_Core\":[\"UI_Text\"],\"UI_UIAutomation\":[\"UI\"],\"UI_UIAutomation_Core\":[\"UI_UIAutomation\"],\"UI_ViewManagement\":[\"UI\"],\"UI_ViewManagement_Core\":[\"UI_ViewManagement\"],\"UI_WebUI\":[\"UI\"],\"UI_WindowManagement\":[\"UI\"],\"UI_WindowManagement_Preview\":[\"UI_WindowManagement\"],\"Wdk\":[\"Win32_Foundation\"],\"Wdk_Devices\":[\"Wdk\"],\"Wdk_Devices_Bluetooth\":[\"Wdk_Devices\"],\"Wdk_Devices_HumanInterfaceDevice\":[\"Wdk_Devices\"],\"Wdk_Foundation\":[\"Wdk\"],\"Wdk_Graphics\":[\"Wdk\"],\"Wdk_Graphics_Direct3D\":[\"Wdk_Graphics\"],\"Wdk_NetworkManagement\":[\"Wdk\"],\"Wdk_NetworkManagement_Ndis\":[\"Wdk_NetworkManagement\"],\"Wdk_NetworkManagement_WindowsFilteringPlatform\":[\"Wdk_NetworkManagement\"],\"Wdk_Storage\":[\"Wdk\"],\"Wdk_Storage_FileSystem\":[\"Wdk_Storage\"],\"Wdk_Storage_FileSystem_Minifilters\":[\"Wdk_Storage_FileSystem\"],\"Wdk_System\":[\"Wdk\"],\"Wdk_System_IO\":[\"Wdk_System\"],\"Wdk_System_Memory\":[\"Wdk_System\"],\"Wdk_System_OfflineRegistry\":[\"Wdk_System\"],\"Wdk_System_Registry\":[\"Wdk_System\"],\"Wdk_System_SystemInformation\":[\"Wdk_System\"],\"Wdk_System_SystemServices\":[\"Wdk_System\"],\"Wdk_System_Threading\":[\"Wdk_System\"],\"Web\":[\"Foundation\"],\"Web_AtomPub\":[\"Web\"],\"Web_Http\":[\"Web\"],\"Web_Http_Diagnostics\":[\"Web_Http\"],\"Web_Http_Filters\":[\"Web_Http\"],\"Web_Http_Headers\":[\"Web_Http\"],\"Web_Syndication\":[\"Web\"],\"Web_UI\":[\"Web\"],\"Web_UI_Interop\":[\"Web_UI\"],\"Win32\":[\"Win32_Foundation\"],\"Win32_AI\":[\"Win32\"],\"Win32_AI_MachineLearning\":[\"Win32_AI\"],\"Win32_AI_MachineLearning_DirectML\":[\"Win32_AI_MachineLearning\"],\"Win32_AI_MachineLearning_WinML\":[\"Win32_AI_MachineLearning\"],\"Win32_Data\":[\"Win32\"],\"Win32_Data_HtmlHelp\":[\"Win32_Data\"],\"Win32_Data_RightsManagement\":[\"Win32_Data\"],\"Win32_Data_Xml\":[\"Win32_Data\"],\"Win32_Data_Xml_MsXml\":[\"Win32_Data_Xml\"],\"Win32_Data_Xml_XmlLite\":[\"Win32_Data_Xml\"],\"Win32_Devices\":[\"Win32\"],\"Win32_Devices_AllJoyn\":[\"Win32_Devices\"],\"Win32_Devices_Beep\":[\"Win32_Devices\"],\"Win32_Devices_BiometricFramework\":[\"Win32_Devices\"],\"Win32_Devices_Bluetooth\":[\"Win32_Devices\"],\"Win32_Devices_Cdrom\":[\"Win32_Devices\"],\"Win32_Devices_Communication\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAccess\":[\"Win32_Devices\"],\"Win32_Devices_DeviceAndDriverInstallation\":[\"Win32_Devices\"],\"Win32_Devices_DeviceQuery\":[\"Win32_Devices\"],\"Win32_Devices_Display\":[\"Win32_Devices\"],\"Win32_Devices_Dvd\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration\":[\"Win32_Devices\"],\"Win32_Devices_Enumeration_Pnp\":[\"Win32_Devices_Enumeration\"],\"Win32_Devices_Fax\":[\"Win32_Devices\"],\"Win32_Devices_FunctionDiscovery\":[\"Win32_Devices\"],\"Win32_Devices_Geolocation\":[\"Win32_Devices\"],\"Win32_Devices_HumanInterfaceDevice\":[\"Win32_Devices\"],\"Win32_Devices_ImageAcquisition\":[\"Win32_Devices\"],\"Win32_Devices_Nfc\":[\"Win32_Devices\"],\"Win32_Devices_Nfp\":[\"Win32_Devices\"],\"Win32_Devices_PortableDevices\":[\"Win32_Devices\"],\"Win32_Devices_Properties\":[\"Win32_Devices\"],\"Win32_Devices_Pwm\":[\"Win32_Devices\"],\"Win32_Devices_Sensors\":[\"Win32_Devices\"],\"Win32_Devices_SerialCommunication\":[\"Win32_Devices\"],\"Win32_Devices_Tapi\":[\"Win32_Devices\"],\"Win32_Devices_Usb\":[\"Win32_Devices\"],\"Win32_Devices_WebServicesOnDevices\":[\"Win32_Devices\"],\"Win32_Foundation\":[\"Win32\"],\"Win32_Gaming\":[\"Win32\"],\"Win32_Globalization\":[\"Win32\"],\"Win32_Graphics\":[\"Win32\"],\"Win32_Graphics_CompositionSwapchain\":[\"Win32_Graphics\"],\"Win32_Graphics_DXCore\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct2D\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct2D_Common\":[\"Win32_Graphics_Direct2D\"],\"Win32_Graphics_Direct3D\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D10\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D11\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D11on12\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D12\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D9\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D9on12\":[\"Win32_Graphics\"],\"Win32_Graphics_Direct3D_Dxc\":[\"Win32_Graphics_Direct3D\"],\"Win32_Graphics_Direct3D_Fxc\":[\"Win32_Graphics_Direct3D\"],\"Win32_Graphics_DirectComposition\":[\"Win32_Graphics\"],\"Win32_Graphics_DirectDraw\":[\"Win32_Graphics\"],\"Win32_Graphics_DirectManipulation\":[\"Win32_Graphics\"],\"Win32_Graphics_DirectWrite\":[\"Win32_Graphics\"],\"Win32_Graphics_Dwm\":[\"Win32_Graphics\"],\"Win32_Graphics_Dxgi\":[\"Win32_Graphics\"],\"Win32_Graphics_Dxgi_Common\":[\"Win32_Graphics_Dxgi\"],\"Win32_Graphics_Gdi\":[\"Win32_Graphics\"],\"Win32_Graphics_GdiPlus\":[\"Win32_Graphics\"],\"Win32_Graphics_Hlsl\":[\"Win32_Graphics\"],\"Win32_Graphics_Imaging\":[\"Win32_Graphics\"],\"Win32_Graphics_Imaging_D2D\":[\"Win32_Graphics_Imaging\"],\"Win32_Graphics_OpenGL\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing\":[\"Win32_Graphics\"],\"Win32_Graphics_Printing_PrintTicket\":[\"Win32_Graphics_Printing\"],\"Win32_Management\":[\"Win32\"],\"Win32_Management_MobileDeviceManagementRegistration\":[\"Win32_Management\"],\"Win32_Media\":[\"Win32\"],\"Win32_Media_Audio\":[\"Win32_Media\"],\"Win32_Media_Audio_Apo\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_DirectMusic\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_DirectSound\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_Endpoints\":[\"Win32_Media_Audio\"],\"Win32_Media_Audio_XAudio2\":[\"Win32_Media_Audio\"],\"Win32_Media_DeviceManager\":[\"Win32_Media\"],\"Win32_Media_DirectShow\":[\"Win32_Media\"],\"Win32_Media_DirectShow_Tv\":[\"Win32_Media_DirectShow\"],\"Win32_Media_DirectShow_Xml\":[\"Win32_Media_DirectShow\"],\"Win32_Media_DxMediaObjects\":[\"Win32_Media\"],\"Win32_Media_KernelStreaming\":[\"Win32_Media\"],\"Win32_Media_LibrarySharingServices\":[\"Win32_Media\"],\"Win32_Media_MediaFoundation\":[\"Win32_Media\"],\"Win32_Media_MediaPlayer\":[\"Win32_Media\"],\"Win32_Media_Multimedia\":[\"Win32_Media\"],\"Win32_Media_PictureAcquisition\":[\"Win32_Media\"],\"Win32_Media_Speech\":[\"Win32_Media\"],\"Win32_Media_Streaming\":[\"Win32_Media\"],\"Win32_Media_WindowsMediaFormat\":[\"Win32_Media\"],\"Win32_NetworkManagement\":[\"Win32\"],\"Win32_NetworkManagement_Dhcp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Dns\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_InternetConnectionWizard\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_IpHelper\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_MobileBroadband\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Multicast\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Ndis\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetBios\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetManagement\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetShell\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkDiagnosticsFramework\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_NetworkPolicyServer\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_P2P\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_QoS\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Rras\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_Snmp\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WNet\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WebDav\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WiFi\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectNow\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsConnectionManager\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFilteringPlatform\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsFirewall\":[\"Win32_NetworkManagement\"],\"Win32_NetworkManagement_WindowsNetworkVirtualization\":[\"Win32_NetworkManagement\"],\"Win32_Networking\":[\"Win32\"],\"Win32_Networking_ActiveDirectory\":[\"Win32_Networking\"],\"Win32_Networking_BackgroundIntelligentTransferService\":[\"Win32_Networking\"],\"Win32_Networking_Clustering\":[\"Win32_Networking\"],\"Win32_Networking_HttpServer\":[\"Win32_Networking\"],\"Win32_Networking_Ldap\":[\"Win32_Networking\"],\"Win32_Networking_NetworkListManager\":[\"Win32_Networking\"],\"Win32_Networking_RemoteDifferentialCompression\":[\"Win32_Networking\"],\"Win32_Networking_WebSocket\":[\"Win32_Networking\"],\"Win32_Networking_WinHttp\":[\"Win32_Networking\"],\"Win32_Networking_WinInet\":[\"Win32_Networking\"],\"Win32_Networking_WinSock\":[\"Win32_Networking\"],\"Win32_Networking_WindowsWebServices\":[\"Win32_Networking\"],\"Win32_Security\":[\"Win32\"],\"Win32_Security_AppLocker\":[\"Win32_Security\"],\"Win32_Security_Authentication\":[\"Win32_Security\"],\"Win32_Security_Authentication_Identity\":[\"Win32_Security_Authentication\"],\"Win32_Security_Authentication_Identity_Provider\":[\"Win32_Security_Authentication_Identity\"],\"Win32_Security_Authorization\":[\"Win32_Security\"],\"Win32_Security_Authorization_UI\":[\"Win32_Security_Authorization\"],\"Win32_Security_ConfigurationSnapin\":[\"Win32_Security\"],\"Win32_Security_Credentials\":[\"Win32_Security\"],\"Win32_Security_Cryptography\":[\"Win32_Security\"],\"Win32_Security_Cryptography_Catalog\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Certificates\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_Sip\":[\"Win32_Security_Cryptography\"],\"Win32_Security_Cryptography_UI\":[\"Win32_Security_Cryptography\"],\"Win32_Security_DiagnosticDataQuery\":[\"Win32_Security\"],\"Win32_Security_DirectoryServices\":[\"Win32_Security\"],\"Win32_Security_EnterpriseData\":[\"Win32_Security\"],\"Win32_Security_ExtensibleAuthenticationProtocol\":[\"Win32_Security\"],\"Win32_Security_Isolation\":[\"Win32_Security\"],\"Win32_Security_LicenseProtection\":[\"Win32_Security\"],\"Win32_Security_NetworkAccessProtection\":[\"Win32_Security\"],\"Win32_Security_Tpm\":[\"Win32_Security\"],\"Win32_Security_WinTrust\":[\"Win32_Security\"],\"Win32_Security_WinWlx\":[\"Win32_Security\"],\"Win32_Storage\":[\"Win32\"],\"Win32_Storage_Cabinets\":[\"Win32_Storage\"],\"Win32_Storage_CloudFilters\":[\"Win32_Storage\"],\"Win32_Storage_Compression\":[\"Win32_Storage\"],\"Win32_Storage_DataDeduplication\":[\"Win32_Storage\"],\"Win32_Storage_DistributedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_EnhancedStorage\":[\"Win32_Storage\"],\"Win32_Storage_FileHistory\":[\"Win32_Storage\"],\"Win32_Storage_FileServerResourceManager\":[\"Win32_Storage\"],\"Win32_Storage_FileSystem\":[\"Win32_Storage\"],\"Win32_Storage_Imapi\":[\"Win32_Storage\"],\"Win32_Storage_IndexServer\":[\"Win32_Storage\"],\"Win32_Storage_InstallableFileSystems\":[\"Win32_Storage\"],\"Win32_Storage_IscsiDisc\":[\"Win32_Storage\"],\"Win32_Storage_Jet\":[\"Win32_Storage\"],\"Win32_Storage_Nvme\":[\"Win32_Storage\"],\"Win32_Storage_OfflineFiles\":[\"Win32_Storage\"],\"Win32_Storage_OperationRecorder\":[\"Win32_Storage\"],\"Win32_Storage_Packaging\":[\"Win32_Storage\"],\"Win32_Storage_Packaging_Appx\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_Packaging_Opc\":[\"Win32_Storage_Packaging\"],\"Win32_Storage_ProjectedFileSystem\":[\"Win32_Storage\"],\"Win32_Storage_StructuredStorage\":[\"Win32_Storage\"],\"Win32_Storage_Vhd\":[\"Win32_Storage\"],\"Win32_Storage_VirtualDiskService\":[\"Win32_Storage\"],\"Win32_Storage_Vss\":[\"Win32_Storage\"],\"Win32_Storage_Xps\":[\"Win32_Storage\"],\"Win32_Storage_Xps_Printing\":[\"Win32_Storage_Xps\"],\"Win32_System\":[\"Win32\"],\"Win32_System_AddressBook\":[\"Win32_System\"],\"Win32_System_Antimalware\":[\"Win32_System\"],\"Win32_System_ApplicationInstallationAndServicing\":[\"Win32_System\"],\"Win32_System_ApplicationVerifier\":[\"Win32_System\"],\"Win32_System_AssessmentTool\":[\"Win32_System\"],\"Win32_System_ClrHosting\":[\"Win32_System\"],\"Win32_System_Com\":[\"Win32_System\"],\"Win32_System_Com_CallObj\":[\"Win32_System_Com\"],\"Win32_System_Com_ChannelCredentials\":[\"Win32_System_Com\"],\"Win32_System_Com_Events\":[\"Win32_System_Com\"],\"Win32_System_Com_Marshal\":[\"Win32_System_Com\"],\"Win32_System_Com_StructuredStorage\":[\"Win32_System_Com\"],\"Win32_System_Com_UI\":[\"Win32_System_Com\"],\"Win32_System_Com_Urlmon\":[\"Win32_System_Com\"],\"Win32_System_ComponentServices\":[\"Win32_System\"],\"Win32_System_Console\":[\"Win32_System\"],\"Win32_System_Contacts\":[\"Win32_System\"],\"Win32_System_CorrelationVector\":[\"Win32_System\"],\"Win32_System_DataExchange\":[\"Win32_System\"],\"Win32_System_DeploymentServices\":[\"Win32_System\"],\"Win32_System_DesktopSharing\":[\"Win32_System\"],\"Win32_System_DeveloperLicensing\":[\"Win32_System\"],\"Win32_System_Diagnostics\":[\"Win32_System\"],\"Win32_System_Diagnostics_Ceip\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ClrProfiling\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_Debug_ActiveScript\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Debug_Extensions\":[\"Win32_System_Diagnostics_Debug\"],\"Win32_System_Diagnostics_Etw\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ProcessSnapshotting\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_ToolHelp\":[\"Win32_System_Diagnostics\"],\"Win32_System_Diagnostics_TraceLogging\":[\"Win32_System_Diagnostics\"],\"Win32_System_DistributedTransactionCoordinator\":[\"Win32_System\"],\"Win32_System_Environment\":[\"Win32_System\"],\"Win32_System_ErrorReporting\":[\"Win32_System\"],\"Win32_System_EventCollector\":[\"Win32_System\"],\"Win32_System_EventLog\":[\"Win32_System\"],\"Win32_System_EventNotificationService\":[\"Win32_System\"],\"Win32_System_GroupPolicy\":[\"Win32_System\"],\"Win32_System_HostCompute\":[\"Win32_System\"],\"Win32_System_HostComputeNetwork\":[\"Win32_System\"],\"Win32_System_HostComputeSystem\":[\"Win32_System\"],\"Win32_System_Hypervisor\":[\"Win32_System\"],\"Win32_System_IO\":[\"Win32_System\"],\"Win32_System_Iis\":[\"Win32_System\"],\"Win32_System_Ioctl\":[\"Win32_System\"],\"Win32_System_JobObjects\":[\"Win32_System\"],\"Win32_System_Js\":[\"Win32_System\"],\"Win32_System_Kernel\":[\"Win32_System\"],\"Win32_System_LibraryLoader\":[\"Win32_System\"],\"Win32_System_Mailslots\":[\"Win32_System\"],\"Win32_System_Mapi\":[\"Win32_System\"],\"Win32_System_Memory\":[\"Win32_System\"],\"Win32_System_Memory_NonVolatile\":[\"Win32_System_Memory\"],\"Win32_System_MessageQueuing\":[\"Win32_System\"],\"Win32_System_MixedReality\":[\"Win32_System\"],\"Win32_System_Mmc\":[\"Win32_System\"],\"Win32_System_Ole\":[\"Win32_System\"],\"Win32_System_ParentalControls\":[\"Win32_System\"],\"Win32_System_PasswordManagement\":[\"Win32_System\"],\"Win32_System_Performance\":[\"Win32_System\"],\"Win32_System_Performance_HardwareCounterProfiling\":[\"Win32_System_Performance\"],\"Win32_System_Pipes\":[\"Win32_System\"],\"Win32_System_Power\":[\"Win32_System\"],\"Win32_System_ProcessStatus\":[\"Win32_System\"],\"Win32_System_RealTimeCommunications\":[\"Win32_System\"],\"Win32_System_Recovery\":[\"Win32_System\"],\"Win32_System_Registry\":[\"Win32_System\"],\"Win32_System_RemoteAssistance\":[\"Win32_System\"],\"Win32_System_RemoteDesktop\":[\"Win32_System\"],\"Win32_System_RemoteManagement\":[\"Win32_System\"],\"Win32_System_RestartManager\":[\"Win32_System\"],\"Win32_System_Restore\":[\"Win32_System\"],\"Win32_System_Rpc\":[\"Win32_System\"],\"Win32_System_Search\":[\"Win32_System\"],\"Win32_System_Search_Common\":[\"Win32_System_Search\"],\"Win32_System_SecurityCenter\":[\"Win32_System\"],\"Win32_System_ServerBackup\":[\"Win32_System\"],\"Win32_System_Services\":[\"Win32_System\"],\"Win32_System_SettingsManagementInfrastructure\":[\"Win32_System\"],\"Win32_System_SetupAndMigration\":[\"Win32_System\"],\"Win32_System_Shutdown\":[\"Win32_System\"],\"Win32_System_SideShow\":[\"Win32_System\"],\"Win32_System_StationsAndDesktops\":[\"Win32_System\"],\"Win32_System_SubsystemForLinux\":[\"Win32_System\"],\"Win32_System_SystemInformation\":[\"Win32_System\"],\"Win32_System_SystemServices\":[\"Win32_System\"],\"Win32_System_TaskScheduler\":[\"Win32_System\"],\"Win32_System_Threading\":[\"Win32_System\"],\"Win32_System_Time\":[\"Win32_System\"],\"Win32_System_TpmBaseServices\":[\"Win32_System\"],\"Win32_System_TransactionServer\":[\"Win32_System\"],\"Win32_System_UpdateAgent\":[\"Win32_System\"],\"Win32_System_UpdateAssessment\":[\"Win32_System\"],\"Win32_System_UserAccessLogging\":[\"Win32_System\"],\"Win32_System_Variant\":[\"Win32_System\"],\"Win32_System_VirtualDosMachines\":[\"Win32_System\"],\"Win32_System_WinRT\":[\"Win32_System\"],\"Win32_System_WinRT_AllJoyn\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Composition\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_CoreInputView\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Direct3D11\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Display\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Graphics\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Graphics_Capture\":[\"Win32_System_WinRT_Graphics\"],\"Win32_System_WinRT_Graphics_Direct2D\":[\"Win32_System_WinRT_Graphics\"],\"Win32_System_WinRT_Graphics_Imaging\":[\"Win32_System_WinRT_Graphics\"],\"Win32_System_WinRT_Holographic\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Isolation\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_ML\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Media\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Metadata\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Pdf\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Printing\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Shell\":[\"Win32_System_WinRT\"],\"Win32_System_WinRT_Storage\":[\"Win32_System_WinRT\"],\"Win32_System_WindowsProgramming\":[\"Win32_System\"],\"Win32_System_WindowsSync\":[\"Win32_System\"],\"Win32_System_Wmi\":[\"Win32_System\"],\"Win32_UI\":[\"Win32\"],\"Win32_UI_Accessibility\":[\"Win32_UI\"],\"Win32_UI_Animation\":[\"Win32_UI\"],\"Win32_UI_ColorSystem\":[\"Win32_UI\"],\"Win32_UI_Controls\":[\"Win32_UI\"],\"Win32_UI_Controls_Dialogs\":[\"Win32_UI_Controls\"],\"Win32_UI_Controls_RichEdit\":[\"Win32_UI_Controls\"],\"Win32_UI_HiDpi\":[\"Win32_UI\"],\"Win32_UI_Input\":[\"Win32_UI\"],\"Win32_UI_Input_Ime\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Ink\":[\"Win32_UI_Input\"],\"Win32_UI_Input_KeyboardAndMouse\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Pointer\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Radial\":[\"Win32_UI_Input\"],\"Win32_UI_Input_Touch\":[\"Win32_UI_Input\"],\"Win32_UI_Input_XboxController\":[\"Win32_UI_Input\"],\"Win32_UI_InteractionContext\":[\"Win32_UI\"],\"Win32_UI_LegacyWindowsEnvironmentFeatures\":[\"Win32_UI\"],\"Win32_UI_Magnification\":[\"Win32_UI\"],\"Win32_UI_Notifications\":[\"Win32_UI\"],\"Win32_UI_Ribbon\":[\"Win32_UI\"],\"Win32_UI_Shell\":[\"Win32_UI\"],\"Win32_UI_Shell_Common\":[\"Win32_UI_Shell\"],\"Win32_UI_Shell_PropertiesSystem\":[\"Win32_UI_Shell\"],\"Win32_UI_TabletPC\":[\"Win32_UI\"],\"Win32_UI_TextServices\":[\"Win32_UI\"],\"Win32_UI_WindowsAndMessaging\":[\"Win32_UI\"],\"Win32_UI_Wpf\":[\"Win32_UI\"],\"Win32_Web\":[\"Win32\"],\"Win32_Web_InternetExplorer\":[\"Win32_Web\"],\"default\":[\"std\"],\"docs\":[],\"std\":[\"windows-collections/std\",\"windows-core/std\",\"windows-future/std\",\"windows-numerics/std\"]}}", "windows_aarch64_gnullvm_0.42.2": "{\"dependencies\":[],\"features\":{}}", "windows_aarch64_gnullvm_0.48.5": "{\"dependencies\":[],\"features\":{}}", "windows_aarch64_gnullvm_0.52.6": "{\"dependencies\":[],\"features\":{}}", - "windows_aarch64_gnullvm_0.53.0": "{\"dependencies\":[],\"features\":{}}", + "windows_aarch64_gnullvm_0.53.1": "{\"dependencies\":[],\"features\":{}}", "windows_aarch64_msvc_0.42.2": "{\"dependencies\":[],\"features\":{}}", "windows_aarch64_msvc_0.48.5": "{\"dependencies\":[],\"features\":{}}", "windows_aarch64_msvc_0.52.6": "{\"dependencies\":[],\"features\":{}}", - "windows_aarch64_msvc_0.53.0": "{\"dependencies\":[],\"features\":{}}", + "windows_aarch64_msvc_0.53.1": "{\"dependencies\":[],\"features\":{}}", "windows_i686_gnu_0.42.2": "{\"dependencies\":[],\"features\":{}}", "windows_i686_gnu_0.48.5": "{\"dependencies\":[],\"features\":{}}", "windows_i686_gnu_0.52.6": "{\"dependencies\":[],\"features\":{}}", - "windows_i686_gnu_0.53.0": "{\"dependencies\":[],\"features\":{}}", + "windows_i686_gnu_0.53.1": "{\"dependencies\":[],\"features\":{}}", "windows_i686_gnullvm_0.52.6": "{\"dependencies\":[],\"features\":{}}", - "windows_i686_gnullvm_0.53.0": "{\"dependencies\":[],\"features\":{}}", + "windows_i686_gnullvm_0.53.1": "{\"dependencies\":[],\"features\":{}}", "windows_i686_msvc_0.42.2": "{\"dependencies\":[],\"features\":{}}", "windows_i686_msvc_0.48.5": "{\"dependencies\":[],\"features\":{}}", "windows_i686_msvc_0.52.6": "{\"dependencies\":[],\"features\":{}}", - "windows_i686_msvc_0.53.0": "{\"dependencies\":[],\"features\":{}}", + "windows_i686_msvc_0.53.1": "{\"dependencies\":[],\"features\":{}}", "windows_x86_64_gnu_0.42.2": "{\"dependencies\":[],\"features\":{}}", "windows_x86_64_gnu_0.48.5": "{\"dependencies\":[],\"features\":{}}", "windows_x86_64_gnu_0.52.6": "{\"dependencies\":[],\"features\":{}}", - "windows_x86_64_gnu_0.53.0": "{\"dependencies\":[],\"features\":{}}", + "windows_x86_64_gnu_0.53.1": "{\"dependencies\":[],\"features\":{}}", "windows_x86_64_gnullvm_0.42.2": "{\"dependencies\":[],\"features\":{}}", "windows_x86_64_gnullvm_0.48.5": "{\"dependencies\":[],\"features\":{}}", "windows_x86_64_gnullvm_0.52.6": "{\"dependencies\":[],\"features\":{}}", - "windows_x86_64_gnullvm_0.53.0": "{\"dependencies\":[],\"features\":{}}", + "windows_x86_64_gnullvm_0.53.1": "{\"dependencies\":[],\"features\":{}}", "windows_x86_64_msvc_0.42.2": "{\"dependencies\":[],\"features\":{}}", "windows_x86_64_msvc_0.48.5": "{\"dependencies\":[],\"features\":{}}", "windows_x86_64_msvc_0.52.6": "{\"dependencies\":[],\"features\":{}}", - "windows_x86_64_msvc_0.53.0": "{\"dependencies\":[],\"features\":{}}", - "winnow_0.7.13": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"annotate-snippets\",\"req\":\"^0.11.3\"},{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.3.2\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.86\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.14\"},{\"kind\":\"dev\",\"name\":\"circular\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"name\":\"is_terminal_polyfill\",\"optional\":true,\"req\":\"^1.48.0\"},{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.0\"},{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.5\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.2.0\"},{\"kind\":\"dev\",\"name\":\"rustc-hash\",\"req\":\"^1.1.0\"},{\"features\":[\"examples\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.21\"},{\"kind\":\"dev\",\"name\":\"term-transcript\",\"req\":\"^0.2.0\"},{\"name\":\"terminal_size\",\"optional\":true,\"req\":\"^0.4.0\"}],\"features\":{\"alloc\":[],\"debug\":[\"std\",\"dep:anstream\",\"dep:anstyle\",\"dep:is_terminal_polyfill\",\"dep:terminal_size\"],\"default\":[\"std\"],\"simd\":[\"dep:memchr\"],\"std\":[\"alloc\",\"memchr?/std\"],\"unstable-doc\":[\"alloc\",\"std\",\"simd\",\"unstable-recover\"],\"unstable-recover\":[]}}", + "windows_x86_64_msvc_0.53.1": "{\"dependencies\":[],\"features\":{}}", + "winnow_0.7.14": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"annotate-snippets\",\"req\":\"^0.11.4\"},{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.15\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.8\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.100\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.15\"},{\"kind\":\"dev\",\"name\":\"circular\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"name\":\"is_terminal_polyfill\",\"optional\":true,\"req\":\"^1.48.1\"},{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.7\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.6.0\"},{\"kind\":\"dev\",\"name\":\"rustc-hash\",\"req\":\"^2.1.1\"},{\"features\":[\"examples\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.21\"},{\"kind\":\"dev\",\"name\":\"term-transcript\",\"req\":\"^0.2.0\"},{\"name\":\"terminal_size\",\"optional\":true,\"req\":\"^0.4.3\"}],\"features\":{\"alloc\":[],\"debug\":[\"std\",\"dep:anstream\",\"dep:anstyle\",\"dep:is_terminal_polyfill\",\"dep:terminal_size\"],\"default\":[\"std\"],\"simd\":[\"dep:memchr\"],\"std\":[\"alloc\",\"memchr?/std\"],\"unstable-doc\":[\"alloc\",\"std\",\"simd\",\"unstable-recover\"],\"unstable-recover\":[]}}", "winreg_0.10.1": "{\"dependencies\":[{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.6\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.3\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"~3.0\"},{\"features\":[\"impl-default\",\"impl-debug\",\"minwindef\",\"minwinbase\",\"timezoneapi\",\"winerror\",\"winnt\",\"winreg\",\"handleapi\"],\"name\":\"winapi\",\"req\":\"^0.3.9\"}],\"features\":{\"serialization-serde\":[\"transactions\",\"serde\"],\"transactions\":[\"winapi/ktmw32\"]}}", "winreg_0.50.0": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.6\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.3\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_bytes\",\"req\":\"^0.11\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"~3.0\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Time\",\"Win32_System_Registry\",\"Win32_Security\",\"Win32_Storage_FileSystem\",\"Win32_System_Diagnostics_Debug\"],\"name\":\"windows-sys\",\"req\":\"^0.48.0\"}],\"features\":{\"serialization-serde\":[\"transactions\",\"serde\"],\"transactions\":[]}}", "winres_0.1.12": "{\"dependencies\":[{\"name\":\"toml\",\"req\":\"^0.5\"},{\"features\":[\"winnt\"],\"kind\":\"dev\",\"name\":\"winapi\",\"req\":\"^0.3\"}],\"features\":{}}", "winsafe_0.0.19": "{\"dependencies\":[],\"features\":{\"comctl\":[\"ole\"],\"dshow\":[\"oleaut\"],\"dwm\":[\"uxtheme\"],\"dxgi\":[\"ole\"],\"gdi\":[\"user\"],\"gui\":[\"comctl\",\"shell\",\"uxtheme\"],\"kernel\":[],\"mf\":[\"oleaut\"],\"ole\":[\"user\"],\"oleaut\":[\"ole\"],\"shell\":[\"oleaut\"],\"taskschd\":[\"oleaut\"],\"user\":[\"kernel\"],\"uxtheme\":[\"gdi\",\"ole\"],\"version\":[\"kernel\"]}}", "winsplit_0.1.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.3\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}", "wiremock_0.6.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"actix-rt\",\"req\":\"^2.10.0\"},{\"name\":\"assert-json-diff\",\"req\":\"^2.0.2\"},{\"features\":[\"attributes\",\"tokio1\"],\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.13.2\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"name\":\"deadpool\",\"req\":\"^0.12.2\"},{\"name\":\"futures\",\"req\":\"^0.3.31\"},{\"name\":\"http\",\"req\":\"^1.3\"},{\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"features\":[\"full\"],\"name\":\"hyper\",\"req\":\"^1.7\"},{\"features\":[\"tokio\",\"server\",\"http1\",\"http2\"],\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"req\":\"^1\"},{\"name\":\"regex\",\"req\":\"^1\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12.23\"},{\"name\":\"serde\",\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1\"},{\"features\":[\"rt\",\"macros\",\"net\"],\"name\":\"tokio\",\"req\":\"^1.47.1\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.47.1\"},{\"name\":\"url\",\"req\":\"^2.5\"}],\"features\":{}}", - "wit-bindgen-rt_0.39.0": "{\"dependencies\":[{\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.3.3\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.30\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.19.0\"}],\"features\":{\"async\":[\"dep:futures\",\"dep:once_cell\"]}}", - "wl-clipboard-rs_0.9.2": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.170\"},{\"name\":\"log\",\"req\":\"^0.4.26\"},{\"features\":[\"io_safety\"],\"name\":\"os_pipe\",\"req\":\"^1.2.1\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.6.0\"},{\"kind\":\"dev\",\"name\":\"proptest-derive\",\"req\":\"^0.5.1\"},{\"features\":[\"fs\",\"event\"],\"name\":\"rustix\",\"req\":\"^0.38.44\"},{\"name\":\"tempfile\",\"req\":\"^3.17.1\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"name\":\"tree_magic_mini\",\"req\":\"^3.1.6\"},{\"name\":\"wayland-backend\",\"req\":\"^0.3.8\"},{\"name\":\"wayland-client\",\"req\":\"^0.31.8\"},{\"features\":[\"client\",\"staging\"],\"name\":\"wayland-protocols\",\"req\":\"^0.32.6\"},{\"features\":[\"server\",\"staging\"],\"kind\":\"dev\",\"name\":\"wayland-protocols\",\"req\":\"^0.32.6\"},{\"features\":[\"client\"],\"name\":\"wayland-protocols-wlr\",\"req\":\"^0.3.6\"},{\"features\":[\"server\"],\"kind\":\"dev\",\"name\":\"wayland-protocols-wlr\",\"req\":\"^0.3.6\"},{\"kind\":\"dev\",\"name\":\"wayland-server\",\"req\":\"^0.31.7\"}],\"features\":{\"dlopen\":[\"native_lib\",\"wayland-backend/dlopen\",\"wayland-backend/dlopen\"],\"native_lib\":[\"wayland-backend/client_system\",\"wayland-backend/server_system\"]}}", + "wit-bindgen_0.51.0": "{\"dependencies\":[{\"name\":\"alloc\",\"optional\":true,\"package\":\"rustc-std-workspace-alloc\",\"req\":\"^1.0\"},{\"name\":\"bitflags\",\"optional\":true,\"req\":\"^2.3.3\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.30\"},{\"name\":\"wit-bindgen-rust-macro\",\"optional\":true,\"req\":\"^0.51.0\"}],\"features\":{\"async\":[\"std\",\"wit-bindgen-rust-macro?/async\"],\"async-spawn\":[\"async\",\"dep:futures\"],\"bitflags\":[\"dep:bitflags\"],\"default\":[\"macros\",\"realloc\",\"async\",\"std\",\"bitflags\"],\"inter-task-wakeup\":[\"async\"],\"macros\":[\"dep:wit-bindgen-rust-macro\"],\"realloc\":[],\"rustc-dep-of-std\":[\"dep:core\",\"dep:alloc\"],\"std\":[]}}", + "wl-clipboard-rs_0.9.3": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2.168\"},{\"name\":\"log\",\"req\":\"^0.4.11\"},{\"features\":[\"io_safety\"],\"name\":\"os_pipe\",\"req\":\"^1.1\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"proptest-derive\",\"req\":\"^0.7\"},{\"features\":[\"fs\",\"event\"],\"name\":\"rustix\",\"req\":\"^1.0.2\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"name\":\"tree_magic_mini\",\"req\":\"^3\"},{\"name\":\"wayland-backend\",\"req\":\"^0.3.11\"},{\"name\":\"wayland-client\",\"req\":\"^0.31.11\"},{\"features\":[\"client\",\"staging\"],\"name\":\"wayland-protocols\",\"req\":\"^0.32.9\"},{\"features\":[\"server\",\"staging\"],\"kind\":\"dev\",\"name\":\"wayland-protocols\",\"req\":\"^0.32.9\"},{\"features\":[\"client\"],\"name\":\"wayland-protocols-wlr\",\"req\":\"^0.3.9\"},{\"features\":[\"server\"],\"kind\":\"dev\",\"name\":\"wayland-protocols-wlr\",\"req\":\"^0.3.9\"},{\"kind\":\"dev\",\"name\":\"wayland-server\",\"req\":\"^0.31.10\"}],\"features\":{\"dlopen\":[\"native_lib\",\"wayland-backend/dlopen\",\"wayland-backend/dlopen\"],\"native_lib\":[\"wayland-backend/client_system\",\"wayland-backend/server_system\"]}}", "writeable_0.6.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"either\",\"optional\":true,\"req\":\"^1.9.0\"},{\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"}],\"features\":{\"alloc\":[],\"default\":[\"alloc\"],\"either\":[\"dep:either\"]}}", - "x11rb-protocol_0.13.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"all-extensions\":[\"composite\",\"damage\",\"dbe\",\"dpms\",\"dri2\",\"dri3\",\"glx\",\"present\",\"randr\",\"record\",\"render\",\"res\",\"screensaver\",\"shape\",\"shm\",\"sync\",\"xevie\",\"xf86dri\",\"xf86vidmode\",\"xfixes\",\"xinerama\",\"xinput\",\"xkb\",\"xprint\",\"xselinux\",\"xtest\",\"xv\",\"xvmc\"],\"composite\":[\"xfixes\"],\"damage\":[\"xfixes\"],\"dbe\":[],\"default\":[\"std\"],\"dpms\":[],\"dri2\":[],\"dri3\":[],\"extra-traits\":[],\"glx\":[],\"present\":[\"randr\",\"xfixes\",\"sync\"],\"randr\":[\"render\"],\"record\":[],\"render\":[],\"request-parsing\":[],\"res\":[],\"resource_manager\":[\"std\"],\"screensaver\":[],\"shape\":[],\"shm\":[],\"std\":[],\"sync\":[],\"xevie\":[],\"xf86dri\":[],\"xf86vidmode\":[],\"xfixes\":[\"render\",\"shape\"],\"xinerama\":[],\"xinput\":[\"xfixes\"],\"xkb\":[],\"xprint\":[],\"xselinux\":[],\"xtest\":[],\"xv\":[\"shm\"],\"xvmc\":[\"xv\"]}}", - "x11rb_0.13.1": "{\"dependencies\":[{\"name\":\"as-raw-xcb-connection\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"gethostname\",\"req\":\"^0.4\",\"target\":\"cfg(not(unix))\"},{\"kind\":\"dev\",\"name\":\"gethostname\",\"req\":\"^0.4\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"libloading\",\"optional\":true,\"req\":\"^0.8.0\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.19\"},{\"kind\":\"dev\",\"name\":\"polling\",\"req\":\"^3.4\"},{\"default_features\":false,\"features\":[\"std\",\"event\",\"fs\",\"net\",\"system\"],\"name\":\"rustix\",\"req\":\"^0.38\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"x11rb-protocol\",\"req\":\"^0.13.1\"}],\"features\":{\"all-extensions\":[\"x11rb-protocol/all-extensions\",\"composite\",\"damage\",\"dbe\",\"dpms\",\"dri2\",\"dri3\",\"glx\",\"present\",\"randr\",\"record\",\"render\",\"res\",\"screensaver\",\"shape\",\"shm\",\"sync\",\"xevie\",\"xf86dri\",\"xf86vidmode\",\"xfixes\",\"xinerama\",\"xinput\",\"xkb\",\"xprint\",\"xselinux\",\"xtest\",\"xv\",\"xvmc\"],\"allow-unsafe-code\":[\"libc\",\"as-raw-xcb-connection\"],\"composite\":[\"x11rb-protocol/composite\",\"xfixes\"],\"cursor\":[\"render\",\"resource_manager\"],\"damage\":[\"x11rb-protocol/damage\",\"xfixes\"],\"dbe\":[\"x11rb-protocol/dbe\"],\"dl-libxcb\":[\"allow-unsafe-code\",\"libloading\",\"once_cell\"],\"dpms\":[\"x11rb-protocol/dpms\"],\"dri2\":[\"x11rb-protocol/dri2\"],\"dri3\":[\"x11rb-protocol/dri3\"],\"extra-traits\":[\"x11rb-protocol/extra-traits\"],\"glx\":[\"x11rb-protocol/glx\"],\"image\":[],\"present\":[\"x11rb-protocol/present\",\"randr\",\"xfixes\",\"sync\"],\"randr\":[\"x11rb-protocol/randr\",\"render\"],\"record\":[\"x11rb-protocol/record\"],\"render\":[\"x11rb-protocol/render\"],\"request-parsing\":[\"x11rb-protocol/request-parsing\"],\"res\":[\"x11rb-protocol/res\"],\"resource_manager\":[\"x11rb-protocol/resource_manager\"],\"screensaver\":[\"x11rb-protocol/screensaver\"],\"shape\":[\"x11rb-protocol/shape\"],\"shm\":[\"x11rb-protocol/shm\"],\"sync\":[\"x11rb-protocol/sync\"],\"xevie\":[\"x11rb-protocol/xevie\"],\"xf86dri\":[\"x11rb-protocol/xf86dri\"],\"xf86vidmode\":[\"x11rb-protocol/xf86vidmode\"],\"xfixes\":[\"x11rb-protocol/xfixes\",\"render\",\"shape\"],\"xinerama\":[\"x11rb-protocol/xinerama\"],\"xinput\":[\"x11rb-protocol/xinput\",\"xfixes\"],\"xkb\":[\"x11rb-protocol/xkb\"],\"xprint\":[\"x11rb-protocol/xprint\"],\"xselinux\":[\"x11rb-protocol/xselinux\"],\"xtest\":[\"x11rb-protocol/xtest\"],\"xv\":[\"x11rb-protocol/xv\",\"shm\"],\"xvmc\":[\"x11rb-protocol/xvmc\",\"xv\"]}}", + "x11rb-protocol_0.13.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"all-extensions\":[\"composite\",\"damage\",\"dbe\",\"dpms\",\"dri2\",\"dri3\",\"glx\",\"present\",\"randr\",\"record\",\"render\",\"res\",\"screensaver\",\"shape\",\"shm\",\"sync\",\"xevie\",\"xf86dri\",\"xf86vidmode\",\"xfixes\",\"xinerama\",\"xinput\",\"xkb\",\"xprint\",\"xselinux\",\"xtest\",\"xv\",\"xvmc\"],\"composite\":[\"xfixes\"],\"damage\":[\"xfixes\"],\"dbe\":[],\"default\":[\"std\"],\"dpms\":[],\"dri2\":[],\"dri3\":[],\"extra-traits\":[],\"glx\":[],\"present\":[\"randr\",\"xfixes\",\"sync\",\"dri3\"],\"randr\":[\"render\"],\"record\":[],\"render\":[],\"request-parsing\":[],\"res\":[],\"resource_manager\":[\"std\"],\"screensaver\":[],\"shape\":[],\"shm\":[],\"std\":[],\"sync\":[],\"xevie\":[],\"xf86dri\":[],\"xf86vidmode\":[],\"xfixes\":[\"render\",\"shape\"],\"xinerama\":[],\"xinput\":[\"xfixes\"],\"xkb\":[],\"xprint\":[],\"xselinux\":[],\"xtest\":[],\"xv\":[\"shm\"],\"xvmc\":[\"xv\"]}}", + "x11rb_0.13.2": "{\"dependencies\":[{\"name\":\"as-raw-xcb-connection\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"gethostname\",\"req\":\"^1.0\"},{\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"libloading\",\"optional\":true,\"req\":\"^0.8.0\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1.19\"},{\"kind\":\"dev\",\"name\":\"polling\",\"req\":\"^3.4\"},{\"name\":\"raw-window-handle\",\"optional\":true,\"req\":\"^0.5.0\"},{\"default_features\":false,\"features\":[\"std\",\"event\",\"fs\",\"net\",\"system\"],\"name\":\"rustix\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"x11rb-protocol\",\"req\":\"^0.13.2\"},{\"name\":\"xcursor\",\"optional\":true,\"req\":\"^0.3.7\"}],\"features\":{\"all-extensions\":[\"x11rb-protocol/all-extensions\",\"composite\",\"damage\",\"dbe\",\"dpms\",\"dri2\",\"dri3\",\"glx\",\"present\",\"randr\",\"record\",\"render\",\"res\",\"screensaver\",\"shape\",\"shm\",\"sync\",\"xevie\",\"xf86dri\",\"xf86vidmode\",\"xfixes\",\"xinerama\",\"xinput\",\"xkb\",\"xprint\",\"xselinux\",\"xtest\",\"xv\",\"xvmc\"],\"allow-unsafe-code\":[\"libc\",\"as-raw-xcb-connection\"],\"composite\":[\"x11rb-protocol/composite\",\"xfixes\"],\"cursor\":[\"render\",\"resource_manager\",\"xcursor\"],\"damage\":[\"x11rb-protocol/damage\",\"xfixes\"],\"dbe\":[\"x11rb-protocol/dbe\"],\"dl-libxcb\":[\"allow-unsafe-code\",\"libloading\",\"once_cell\"],\"dpms\":[\"x11rb-protocol/dpms\"],\"dri2\":[\"x11rb-protocol/dri2\"],\"dri3\":[\"x11rb-protocol/dri3\"],\"extra-traits\":[\"x11rb-protocol/extra-traits\"],\"glx\":[\"x11rb-protocol/glx\"],\"image\":[],\"present\":[\"x11rb-protocol/present\",\"randr\",\"xfixes\",\"sync\"],\"randr\":[\"x11rb-protocol/randr\",\"render\"],\"record\":[\"x11rb-protocol/record\"],\"render\":[\"x11rb-protocol/render\"],\"request-parsing\":[\"x11rb-protocol/request-parsing\"],\"res\":[\"x11rb-protocol/res\"],\"resource_manager\":[\"x11rb-protocol/resource_manager\"],\"screensaver\":[\"x11rb-protocol/screensaver\"],\"shape\":[\"x11rb-protocol/shape\"],\"shm\":[\"x11rb-protocol/shm\"],\"sync\":[\"x11rb-protocol/sync\"],\"xevie\":[\"x11rb-protocol/xevie\"],\"xf86dri\":[\"x11rb-protocol/xf86dri\"],\"xf86vidmode\":[\"x11rb-protocol/xf86vidmode\"],\"xfixes\":[\"x11rb-protocol/xfixes\",\"render\",\"shape\"],\"xinerama\":[\"x11rb-protocol/xinerama\"],\"xinput\":[\"x11rb-protocol/xinput\",\"xfixes\"],\"xkb\":[\"x11rb-protocol/xkb\"],\"xprint\":[\"x11rb-protocol/xprint\"],\"xselinux\":[\"x11rb-protocol/xselinux\"],\"xtest\":[\"x11rb-protocol/xtest\"],\"xv\":[\"x11rb-protocol/xv\",\"shm\"],\"xvmc\":[\"x11rb-protocol/xvmc\",\"xv\"]}}", + "x25519-dalek_2.0.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"curve25519-dalek\",\"req\":\"^4\"},{\"default_features\":false,\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"default_features\":false,\"features\":[\"getrandom\"],\"kind\":\"dev\",\"name\":\"rand_core\",\"req\":\"^0.6\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"zeroize_derive\"],\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[\"curve25519-dalek/alloc\",\"serde?/alloc\",\"zeroize?/alloc\"],\"default\":[\"alloc\",\"precomputed-tables\",\"zeroize\"],\"getrandom\":[\"rand_core/getrandom\"],\"precomputed-tables\":[\"curve25519-dalek/precomputed-tables\"],\"reusable_secrets\":[],\"serde\":[\"dep:serde\",\"curve25519-dalek/serde\"],\"static_secrets\":[],\"zeroize\":[\"dep:zeroize\",\"curve25519-dalek/zeroize\"]}}", "xdg-home_1.3.0": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"features\":[\"Win32_Foundation\",\"Win32_UI_Shell\",\"Win32_System_Com\"],\"name\":\"windows-sys\",\"req\":\"^0.59\",\"target\":\"cfg(windows)\"}],\"features\":{}}", + "xz2_0.1.7": "{\"dependencies\":[{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.1.26\"},{\"name\":\"lzma-sys\",\"req\":\"^0.1.18\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.0\"},{\"kind\":\"dev\",\"name\":\"tokio-core\",\"req\":\"^0.1.17\"},{\"name\":\"tokio-io\",\"optional\":true,\"req\":\"^0.1.12\"}],\"features\":{\"static\":[\"lzma-sys/static\"],\"tokio\":[\"tokio-io\",\"futures\"]}}", "yansi_1.0.1": "{\"dependencies\":[{\"name\":\"is-terminal\",\"optional\":true,\"req\":\"^0.4.11\"}],\"features\":{\"_nightly\":[],\"alloc\":[],\"default\":[\"std\"],\"detect-env\":[\"std\"],\"detect-tty\":[\"is-terminal\",\"std\"],\"hyperlink\":[\"std\"],\"std\":[\"alloc\"]}}", - "yoke-derive_0.8.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.61\"},{\"name\":\"quote\",\"req\":\"^1.0.28\"},{\"features\":[\"fold\"],\"name\":\"syn\",\"req\":\"^2.0.21\"},{\"name\":\"synstructure\",\"req\":\"^0.13.0\"}],\"features\":{}}", - "yoke_0.8.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.110\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.110\"},{\"default_features\":false,\"name\":\"stable_deref_trait\",\"req\":\"^1.2.0\"},{\"default_features\":false,\"name\":\"yoke-derive\",\"optional\":true,\"req\":\"^0.8.0\"},{\"default_features\":false,\"name\":\"zerofrom\",\"optional\":true,\"req\":\"^0.1.3\"}],\"features\":{\"alloc\":[\"stable_deref_trait/alloc\",\"serde?/alloc\",\"zerofrom/alloc\"],\"default\":[\"alloc\",\"zerofrom\"],\"derive\":[\"dep:yoke-derive\",\"zerofrom/derive\"],\"serde\":[\"dep:serde\"],\"zerofrom\":[\"dep:zerofrom\"]}}", + "yoke-derive_0.8.1": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.61\"},{\"name\":\"quote\",\"req\":\"^1.0.28\"},{\"features\":[\"fold\"],\"name\":\"syn\",\"req\":\"^2.0.21\"},{\"name\":\"synstructure\",\"req\":\"^0.13.0\"}],\"features\":{}}", + "yoke_0.8.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.220\"},{\"default_features\":false,\"name\":\"stable_deref_trait\",\"req\":\"^1.2.0\"},{\"default_features\":false,\"name\":\"yoke-derive\",\"optional\":true,\"req\":\"^0.8.0\"},{\"default_features\":false,\"name\":\"zerofrom\",\"optional\":true,\"req\":\"^0.1.3\"}],\"features\":{\"alloc\":[\"stable_deref_trait/alloc\",\"zerofrom/alloc\"],\"default\":[\"alloc\",\"zerofrom\"],\"derive\":[\"dep:yoke-derive\",\"zerofrom/derive\"],\"serde\":[],\"zerofrom\":[\"dep:zerofrom\"]}}", "zbus_4.4.0": "{\"dependencies\":[{\"name\":\"async-broadcast\",\"req\":\"^0.7.0\"},{\"name\":\"async-executor\",\"optional\":true,\"req\":\"^1.11.0\"},{\"name\":\"async-fs\",\"optional\":true,\"req\":\"^2.1.2\"},{\"name\":\"async-io\",\"optional\":true,\"req\":\"^2.3.2\"},{\"name\":\"async-lock\",\"optional\":true,\"req\":\"^3.3.0\"},{\"name\":\"async-process\",\"req\":\"^2.2.2\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"name\":\"async-recursion\",\"req\":\"^1.1.1\",\"target\":\"cfg(any(target_os = \\\"macos\\\", windows))\"},{\"name\":\"async-task\",\"optional\":true,\"req\":\"^4.7.1\"},{\"name\":\"async-trait\",\"req\":\"^0.1.80\"},{\"name\":\"blocking\",\"optional\":true,\"req\":\"^1.6.0\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.3\"},{\"features\":[\"serde\"],\"name\":\"enumflags2\",\"req\":\"^0.7.9\"},{\"name\":\"event-listener\",\"req\":\"^5.3.0\"},{\"name\":\"futures-core\",\"req\":\"^0.3.30\"},{\"name\":\"futures-sink\",\"req\":\"^0.3.30\"},{\"default_features\":false,\"features\":[\"sink\",\"std\"],\"name\":\"futures-util\",\"req\":\"^0.3.30\"},{\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.30\"},{\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"default_features\":false,\"features\":[\"socket\",\"uio\",\"user\"],\"name\":\"nix\",\"req\":\"^0.29\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"ntest\",\"req\":\"^0.9.2\"},{\"name\":\"ordered-stream\",\"req\":\"^0.2\"},{\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.200\"},{\"name\":\"serde_repr\",\"req\":\"^0.1.19\"},{\"features\":[\"std\"],\"name\":\"sha1\",\"req\":\"^0.10.6\"},{\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.10.1\"},{\"default_features\":false,\"features\":[\"trace\"],\"kind\":\"dev\",\"name\":\"test-log\",\"req\":\"^0.2.16\"},{\"features\":[\"rt\",\"net\",\"time\",\"fs\",\"io-util\",\"process\",\"sync\",\"tracing\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.37.0\"},{\"features\":[\"macros\",\"rt-multi-thread\",\"fs\",\"io-util\",\"net\",\"sync\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.37.0\"},{\"name\":\"tokio-vsock\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"tracing\",\"req\":\"^0.1.40\"},{\"default_features\":false,\"features\":[\"env-filter\",\"fmt\",\"ansi\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3.18\"},{\"name\":\"uds_windows\",\"req\":\"^1.1.0\",\"target\":\"cfg(windows)\"},{\"name\":\"vsock\",\"optional\":true,\"req\":\"^0.5.0\"},{\"features\":[\"Win32_Foundation\",\"Win32_Security_Authorization\",\"Win32_System_Memory\",\"Win32_Networking\",\"Win32_Networking_WinSock\",\"Win32_NetworkManagement\",\"Win32_NetworkManagement_IpHelper\",\"Win32_System_Threading\"],\"name\":\"windows-sys\",\"req\":\"^0.52\",\"target\":\"cfg(windows)\"},{\"name\":\"xdg-home\",\"req\":\"^1.1.0\"},{\"name\":\"zbus_macros\",\"req\":\"=4.4.0\"},{\"name\":\"zbus_names\",\"req\":\"^3.0\"},{\"kind\":\"dev\",\"name\":\"zbus_xml\",\"req\":\"^4.0.0\"},{\"default_features\":false,\"features\":[\"enumflags2\"],\"name\":\"zvariant\",\"req\":\"^4.2.0\"}],\"features\":{\"async-io\":[\"dep:async-io\",\"async-executor\",\"async-task\",\"async-lock\",\"async-fs\",\"blocking\",\"futures-util/io\"],\"bus-impl\":[\"p2p\"],\"chrono\":[\"zvariant/chrono\"],\"default\":[\"async-io\"],\"heapless\":[\"zvariant/heapless\"],\"option-as-array\":[\"zvariant/option-as-array\"],\"p2p\":[],\"time\":[\"zvariant/time\"],\"tokio\":[\"dep:tokio\"],\"tokio-vsock\":[\"dep:tokio-vsock\",\"tokio\"],\"url\":[\"zvariant/url\"],\"uuid\":[\"zvariant/uuid\"],\"vsock\":[\"dep:vsock\",\"dep:async-io\"]}}", "zbus_macros_4.4.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-io\",\"req\":\"^2.3.2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.30\"},{\"name\":\"proc-macro-crate\",\"req\":\"^3.1.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.81\"},{\"name\":\"quote\",\"req\":\"^1.0.36\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.15\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.200\"},{\"features\":[\"extra-traits\",\"fold\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0.64\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.93\"},{\"name\":\"zvariant_utils\",\"req\":\"=2.1.0\"}],\"features\":{}}", "zbus_names_3.0.0": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"default_features\":false,\"features\":[\"enumflags2\"],\"name\":\"zvariant\",\"req\":\"^4.0.0\"}],\"features\":{}}", - "zerocopy-derive_0.8.26": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"dissimilar\",\"req\":\"^1.0.9\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"=0.2.163\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"=1.9\"},{\"kind\":\"dev\",\"name\":\"prettyplease\",\"req\":\"=0.2.17\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.1\"},{\"name\":\"quote\",\"req\":\"^1.0.10\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.46\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"=1.0.89\"}],\"features\":{}}", - "zerocopy_0.8.26": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"either\",\"req\":\"=1.13.0\"},{\"kind\":\"dev\",\"name\":\"elain\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.11\"},{\"default_features\":false,\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"=1.0.89\"},{\"name\":\"zerocopy-derive\",\"req\":\"=0.8.26\",\"target\":\"cfg(any())\"},{\"name\":\"zerocopy-derive\",\"optional\":true,\"req\":\"=0.8.26\"},{\"kind\":\"dev\",\"name\":\"zerocopy-derive\",\"req\":\"=0.8.26\"}],\"features\":{\"__internal_use_only_features_that_work_on_stable\":[\"alloc\",\"derive\",\"simd\",\"std\"],\"alloc\":[],\"derive\":[\"zerocopy-derive\"],\"float-nightly\":[],\"simd\":[],\"simd-nightly\":[\"simd\"],\"std\":[\"alloc\"]}}", + "zerocopy-derive_0.8.37": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"dissimilar\",\"req\":\"^1.0.9\"},{\"kind\":\"dev\",\"name\":\"prettyplease\",\"req\":\"=0.2.17\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.1\"},{\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"=1.0.80\"},{\"name\":\"quote\",\"req\":\"^1.0.40\"},{\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"=1.0.40\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2.0.46\"},{\"features\":[\"visit\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0.46\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"=1.0.89\"}],\"features\":{}}", + "zerocopy_0.8.37": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"elain\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"itertools\",\"req\":\"^0.11\"},{\"default_features\":false,\"features\":[\"small_rng\"],\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"=1.0.89\"},{\"name\":\"zerocopy-derive\",\"req\":\"=0.8.37\",\"target\":\"cfg(any())\"},{\"name\":\"zerocopy-derive\",\"optional\":true,\"req\":\"=0.8.37\"},{\"kind\":\"dev\",\"name\":\"zerocopy-derive\",\"req\":\"=0.8.37\"}],\"features\":{\"__internal_use_only_features_that_work_on_stable\":[\"alloc\",\"derive\",\"simd\",\"std\"],\"alloc\":[],\"derive\":[\"zerocopy-derive\"],\"float-nightly\":[],\"simd\":[],\"simd-nightly\":[\"simd\"],\"std\":[\"alloc\"]}}", "zerofrom-derive_0.1.6": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.61\"},{\"name\":\"quote\",\"req\":\"^1.0.28\"},{\"features\":[\"fold\"],\"name\":\"syn\",\"req\":\"^2.0.21\"},{\"name\":\"synstructure\",\"req\":\"^0.13.0\"}],\"features\":{}}", "zerofrom_0.1.6": "{\"dependencies\":[{\"default_features\":false,\"name\":\"zerofrom-derive\",\"optional\":true,\"req\":\"^0.1.3\"}],\"features\":{\"alloc\":[],\"default\":[\"alloc\"],\"derive\":[\"dep:zerofrom-derive\"]}}", "zeroize_1.8.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"zeroize_derive\",\"optional\":true,\"req\":\"^1.3\"}],\"features\":{\"aarch64\":[],\"alloc\":[],\"default\":[\"alloc\"],\"derive\":[\"zeroize_derive\"],\"simd\":[],\"std\":[\"alloc\"]}}", - "zeroize_derive_1.4.2": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\",\"extra-traits\",\"visit\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", - "zerotrie_0.2.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2.3\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"icu_locale_core\",\"req\":\"^2.0.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"litemap\",\"optional\":true,\"req\":\"^0.8.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rand_pcg\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"rmp-serde\",\"req\":\"^1.2.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.110\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.110\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"yoke\",\"optional\":true,\"req\":\"^0.8.0\"},{\"default_features\":false,\"name\":\"zerofrom\",\"optional\":true,\"req\":\"^0.1.3\"},{\"default_features\":false,\"name\":\"zerovec\",\"optional\":true,\"req\":\"^0.11.1\"}],\"features\":{\"alloc\":[],\"databake\":[\"dep:databake\",\"zerovec?/databake\"],\"default\":[],\"litemap\":[\"dep:litemap\",\"alloc\"],\"serde\":[\"dep:serde\",\"dep:litemap\",\"alloc\",\"litemap/serde\",\"zerovec?/serde\"],\"yoke\":[\"dep:yoke\"],\"zerofrom\":[\"dep:zerofrom\"]}}", - "zerovec-derive_0.11.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.61\"},{\"name\":\"quote\",\"req\":\"^1.0.28\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.110\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"features\":[\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.21\"}],\"features\":{}}", + "zeroize_derive_1.4.3": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\",\"extra-traits\",\"visit\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{}}", + "zerotrie_0.2.3": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2.3\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"litemap\",\"optional\":true,\"req\":\"^0.8.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"yoke\",\"optional\":true,\"req\":\"^0.8.0\"},{\"default_features\":false,\"name\":\"zerofrom\",\"optional\":true,\"req\":\"^0.1.3\"},{\"default_features\":false,\"name\":\"zerovec\",\"optional\":true,\"req\":\"^0.11.3\"}],\"features\":{\"alloc\":[],\"databake\":[\"dep:databake\",\"zerovec?/databake\"],\"default\":[],\"litemap\":[\"dep:litemap\",\"alloc\"],\"serde\":[\"dep:serde_core\",\"dep:litemap\",\"alloc\",\"litemap/serde\",\"zerovec?/serde\"],\"yoke\":[\"dep:yoke\"],\"zerofrom\":[\"dep:zerofrom\"]}}", + "zerovec-derive_0.11.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.61\"},{\"name\":\"quote\",\"req\":\"^1.0.28\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"features\":[\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.21\"}],\"features\":{}}", "zerovec_0.11.5": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"features\":[\"xxhash64\"],\"name\":\"twox-hash\",\"optional\":true,\"req\":\"^2.0.0\"},{\"default_features\":false,\"name\":\"yoke\",\"optional\":true,\"req\":\"^0.8.0\"},{\"default_features\":false,\"name\":\"zerofrom\",\"req\":\"^0.1.3\"},{\"default_features\":false,\"name\":\"zerovec-derive\",\"optional\":true,\"req\":\"^0.11.1\"}],\"features\":{\"alloc\":[\"serde?/alloc\"],\"databake\":[\"dep:databake\"],\"derive\":[\"dep:zerovec-derive\"],\"hashmap\":[\"dep:twox-hash\",\"alloc\"],\"serde\":[\"dep:serde\"],\"std\":[],\"yoke\":[\"dep:yoke\"]}}", + "zip_2.4.2": "{\"dependencies\":[{\"name\":\"aes\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.95\"},{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"req\":\"^1.4.1\",\"target\":\"cfg(fuzzing)\"},{\"kind\":\"dev\",\"name\":\"bencher\",\"req\":\"^0.1.5\"},{\"name\":\"bzip2\",\"optional\":true,\"req\":\"^0.5.0\"},{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"=4.4.18\"},{\"name\":\"constant_time_eq\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"crc32fast\",\"req\":\"^1.4\"},{\"name\":\"crossbeam-utils\",\"req\":\"^0.8.21\",\"target\":\"cfg(any(all(target_arch = \\\"arm\\\", target_pointer_width = \\\"32\\\"), target_arch = \\\"mips\\\", target_arch = \\\"powerpc\\\"))\"},{\"name\":\"deflate64\",\"optional\":true,\"req\":\"^0.1.9\"},{\"default_features\":false,\"name\":\"displaydoc\",\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"wasm_js\",\"std\"],\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.1\"},{\"features\":[\"wasm_js\",\"std\"],\"kind\":\"dev\",\"name\":\"getrandom\",\"req\":\"^0.3.1\"},{\"features\":[\"reset\"],\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"indexmap\",\"req\":\"^2\"},{\"default_features\":false,\"name\":\"lzma-rs\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"memchr\",\"req\":\"^2.7\"},{\"default_features\":false,\"name\":\"nt-time\",\"optional\":true,\"req\":\"^0.10.6\"},{\"name\":\"pbkdf2\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"sha1\",\"optional\":true,\"req\":\"^0.10\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.15\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.37\"},{\"default_features\":false,\"features\":[\"formatting\",\"macros\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3.37\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.5\"},{\"name\":\"xz2\",\"optional\":true,\"req\":\"^0.1.7\"},{\"features\":[\"zeroize_derive\"],\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.8\"},{\"name\":\"zopfli\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"zstd\",\"optional\":true,\"req\":\"^0.13\"}],\"features\":{\"_all-features\":[],\"_deflate-any\":[],\"aes-crypto\":[\"aes\",\"constant_time_eq\",\"hmac\",\"pbkdf2\",\"sha1\",\"getrandom\",\"zeroize\"],\"chrono\":[\"chrono/default\"],\"default\":[\"aes-crypto\",\"bzip2\",\"deflate64\",\"deflate\",\"lzma\",\"time\",\"zstd\",\"xz\"],\"deflate\":[\"flate2/rust_backend\",\"deflate-zopfli\",\"deflate-flate2\"],\"deflate-flate2\":[\"_deflate-any\"],\"deflate-miniz\":[\"deflate\",\"deflate-flate2\"],\"deflate-zlib\":[\"flate2/zlib\",\"deflate-flate2\"],\"deflate-zlib-ng\":[\"flate2/zlib-ng\",\"deflate-flate2\"],\"deflate-zopfli\":[\"zopfli\",\"_deflate-any\"],\"lzma\":[\"lzma-rs/stream\"],\"nt-time\":[\"dep:nt-time\"],\"unreserved\":[],\"xz\":[\"dep:xz2\"]}}", + "zmij_1.0.19": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8\",\"target\":\"cfg(not(miri))\"},{\"name\":\"no-panic\",\"optional\":true,\"req\":\"^0.1.36\"},{\"kind\":\"dev\",\"name\":\"num-bigint\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"num-integer\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"num_cpus\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"opt-level\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"ryu\",\"req\":\"^1\"}],\"features\":{}}", + "zopfli_0.8.3": "{\"dependencies\":[{\"name\":\"bumpalo\",\"req\":\"^3.19.0\"},{\"default_features\":false,\"name\":\"crc32fast\",\"optional\":true,\"req\":\"^1.5.0\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.28\"},{\"kind\":\"dev\",\"name\":\"miniz_oxide\",\"req\":\"^0.8.9\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.7.0\"},{\"kind\":\"dev\",\"name\":\"proptest-derive\",\"req\":\"^0.6.0\"},{\"default_features\":false,\"name\":\"simd-adler32\",\"optional\":true,\"req\":\"^0.3.7\"}],\"features\":{\"default\":[\"gzip\",\"std\",\"zlib\"],\"gzip\":[\"dep:crc32fast\"],\"nightly\":[\"crc32fast?/nightly\"],\"std\":[\"crc32fast?/std\",\"dep:log\",\"simd-adler32?/std\"],\"zlib\":[\"dep:simd-adler32\"]}}", "zstd-safe_7.2.4": "{\"dependencies\":[{\"default_features\":false,\"name\":\"zstd-sys\",\"req\":\"^2.0.15\"}],\"features\":{\"arrays\":[],\"bindgen\":[\"zstd-sys/bindgen\"],\"debug\":[\"zstd-sys/debug\"],\"default\":[\"legacy\",\"arrays\",\"zdict_builder\"],\"doc-cfg\":[],\"experimental\":[\"zstd-sys/experimental\"],\"fat-lto\":[\"zstd-sys/fat-lto\"],\"legacy\":[\"zstd-sys/legacy\"],\"no_asm\":[\"zstd-sys/no_asm\"],\"pkg-config\":[\"zstd-sys/pkg-config\"],\"seekable\":[\"zstd-sys/seekable\"],\"std\":[\"zstd-sys/std\"],\"thin\":[\"zstd-sys/thin\"],\"thin-lto\":[\"zstd-sys/thin-lto\"],\"zdict_builder\":[\"zstd-sys/zdict_builder\"],\"zstdmt\":[\"zstd-sys/zstdmt\"]}}", "zstd-sys_2.0.16+zstd.1.5.7": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"runtime\"],\"kind\":\"build\",\"name\":\"bindgen\",\"optional\":true,\"req\":\"^0.72\"},{\"features\":[\"parallel\"],\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.0.45\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3.28\"}],\"features\":{\"debug\":[],\"default\":[\"legacy\",\"zdict_builder\",\"bindgen\"],\"experimental\":[],\"fat-lto\":[],\"legacy\":[],\"no_asm\":[],\"no_wasm_shim\":[],\"non-cargo\":[],\"pkg-config\":[],\"seekable\":[],\"std\":[],\"thin\":[],\"thin-lto\":[],\"zdict_builder\":[],\"zstdmt\":[]}}", "zstd_0.13.3": "{\"dependencies\":[{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.0\"},{\"kind\":\"dev\",\"name\":\"humansize\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"partial-io\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.2\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"zstd-safe\",\"req\":\"^7.1.0\"}],\"features\":{\"arrays\":[\"zstd-safe/arrays\"],\"bindgen\":[\"zstd-safe/bindgen\"],\"debug\":[\"zstd-safe/debug\"],\"default\":[\"legacy\",\"arrays\",\"zdict_builder\"],\"doc-cfg\":[],\"experimental\":[\"zstd-safe/experimental\"],\"fat-lto\":[\"zstd-safe/fat-lto\"],\"legacy\":[\"zstd-safe/legacy\"],\"no_asm\":[\"zstd-safe/no_asm\"],\"pkg-config\":[\"zstd-safe/pkg-config\"],\"thin\":[\"zstd-safe/thin\"],\"thin-lto\":[\"zstd-safe/thin-lto\"],\"wasm\":[],\"zdict_builder\":[\"zstd-safe/zdict_builder\"],\"zstdmt\":[\"zstd-safe/zstdmt\"]}}", "zune-core_0.4.12": "{\"dependencies\":[{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.17\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.52\"}],\"features\":{\"std\":[]}}", - "zune-core_0.5.0": "{\"dependencies\":[{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"std\":[]}}", - "zune-jpeg_0.4.19": "{\"dependencies\":[{\"name\":\"zune-core\",\"req\":\"^0.4\"}],\"features\":{\"default\":[\"x86\",\"neon\",\"std\"],\"log\":[\"zune-core/log\"],\"neon\":[],\"std\":[\"zune-core/std\"],\"x86\":[]}}", - "zune-jpeg_0.5.5": "{\"dependencies\":[{\"name\":\"zune-core\",\"req\":\"^0.5\"}],\"features\":{\"default\":[\"x86\",\"neon\",\"std\"],\"log\":[\"zune-core/log\"],\"neon\":[],\"std\":[\"zune-core/std\"],\"x86\":[]}}", + "zune-core_0.5.1": "{\"dependencies\":[{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"std\":[]}}", + "zune-jpeg_0.4.21": "{\"dependencies\":[{\"name\":\"zune-core\",\"req\":\"^0.4\"}],\"features\":{\"default\":[\"x86\",\"neon\",\"std\"],\"log\":[\"zune-core/log\"],\"neon\":[],\"std\":[\"zune-core/std\"],\"x86\":[]}}", + "zune-jpeg_0.5.12": "{\"dependencies\":[{\"name\":\"zune-core\",\"req\":\"^0.5.1\"}],\"features\":{\"default\":[\"x86\",\"neon\",\"std\"],\"log\":[\"zune-core/log\"],\"neon\":[],\"portable_simd\":[],\"std\":[\"zune-core/std\"],\"x86\":[]}}", "zvariant_4.2.0": "{\"dependencies\":[{\"features\":[\"serde\"],\"name\":\"arrayvec\",\"optional\":true,\"req\":\"^0.7.4\"},{\"default_features\":false,\"features\":[\"serde\"],\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.38\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"name\":\"endi\",\"req\":\"^1.1.0\"},{\"features\":[\"serde\"],\"name\":\"enumflags2\",\"optional\":true,\"req\":\"^0.7.9\"},{\"kind\":\"dev\",\"name\":\"glib\",\"req\":\"^0.20.0\"},{\"features\":[\"serde\"],\"name\":\"heapless\",\"optional\":true,\"req\":\"^0.8.0\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.5\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0.200\"},{\"name\":\"serde_bytes\",\"optional\":true,\"req\":\"^0.11.14\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.116\"},{\"kind\":\"dev\",\"name\":\"serde_repr\",\"req\":\"^0.1.19\"},{\"name\":\"static_assertions\",\"req\":\"^1.1.0\"},{\"features\":[\"serde\"],\"name\":\"time\",\"optional\":true,\"req\":\"^0.3.36\"},{\"features\":[\"serde\"],\"name\":\"url\",\"optional\":true,\"req\":\"^2.5.0\"},{\"features\":[\"serde\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1.8.0\"},{\"name\":\"zvariant_derive\",\"req\":\"=4.2.0\"}],\"features\":{\"default\":[],\"gvariant\":[],\"option-as-array\":[],\"ostree-tests\":[\"gvariant\"]}}", "zvariant_derive_4.2.0": "{\"dependencies\":[{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"enumflags2\",\"req\":\"^0.7.9\"},{\"name\":\"proc-macro-crate\",\"req\":\"^3.1.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.81\"},{\"name\":\"quote\",\"req\":\"^1.0.36\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.200\"},{\"kind\":\"dev\",\"name\":\"serde_repr\",\"req\":\"^0.1.19\"},{\"features\":[\"extra-traits\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0.64\"},{\"name\":\"zvariant_utils\",\"req\":\"=2.1.0\"}],\"features\":{}}", "zvariant_utils_2.1.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.81\"},{\"name\":\"quote\",\"req\":\"^1.0.36\"},{\"features\":[\"extra-traits\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0.64\"}],\"features\":{}}" diff --git a/announcement_tip.toml b/announcement_tip.toml index b2cda2f044..0726fe237a 100644 --- a/announcement_tip.toml +++ b/announcement_tip.toml @@ -15,3 +15,9 @@ target_app = "cli" content = "This is a test announcement" version_regex = "^0\\.0\\.0$" to_date = "2026-05-10" + +[[announcements]] +content = "**BREAKING NEWS**: `gpt-5.3-codex` is out! Upgrade to `0.98.0` for a faster, smarter, more steerable agent." +from_date = "2026-02-01" +to_date = "2026-02-15" +version_regex = "^0\\.(?:[0-9]|[1-8][0-9]|9[0-7])\\." diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index a2438fa5c2..aada452746 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1219,6 +1219,7 @@ dependencies = [ "axum", "base64 0.22.1", "chrono", + "clap", "codex-app-server-protocol", "codex-arg0", "codex-backend-client", @@ -1233,8 +1234,10 @@ dependencies = [ "codex-protocol", "codex-rmcp-client", "codex-utils-absolute-path", + "codex-utils-cargo-bin", "codex-utils-json-to-toml", "core_test_support", + "futures", "os_info", "pretty_assertions", "rmcp", @@ -1245,6 +1248,7 @@ dependencies = [ "tempfile", "time", "tokio", + "tokio-tungstenite", "toml 0.9.11+spec-1.1.0", "tracing", "tracing-subscriber", @@ -1379,6 +1383,7 @@ dependencies = [ "clap_complete", "codex-app-server", "codex-app-server-protocol", + "codex-app-server-test-client", "codex-arg0", "codex-chatgpt", "codex-cloud-tasks", @@ -1564,6 +1569,7 @@ dependencies = [ "libc", "maplit", "multimap", + "notify", "once_cell", "openssl-sys", "opentelemetry_sdk", @@ -1797,7 +1803,6 @@ dependencies = [ "serde_json", "tempfile", "tokio", - "which", ] [[package]] @@ -1917,9 +1922,9 @@ version = "0.0.0" dependencies = [ "chrono", "codex-api", - "codex-app-server-protocol", "codex-protocol", "codex-utils-absolute-path", + "codex-utils-string", "eventsource-stream", "http 1.4.0", "opentelemetry", @@ -1927,6 +1932,7 @@ dependencies = [ "opentelemetry-otlp", "opentelemetry-semantic-conventions", "opentelemetry_sdk", + "os_info", "pretty_assertions", "reqwest", "serde", @@ -2244,6 +2250,9 @@ dependencies = [ [[package]] name = "codex-utils-string" version = "0.0.0" +dependencies = [ + "pretty_assertions", +] [[package]] name = "codex-windows-sandbox" diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 2c92f0b463..39ebe5967d 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -70,6 +70,7 @@ codex-ansi-escape = { path = "ansi-escape" } codex-api = { path = "codex-api" } codex-app-server = { path = "app-server" } codex-app-server-protocol = { path = "app-server-protocol" } +codex-app-server-test-client = { path = "app-server-test-client" } codex-apply-patch = { path = "apply-patch" } codex-arg0 = { path = "arg0" } codex-async-utils = { path = "async-utils" } diff --git a/codex-rs/README.md b/codex-rs/README.md index cbe1fe3779..9cdd772b2a 100644 --- a/codex-rs/README.md +++ b/codex-rs/README.md @@ -51,6 +51,7 @@ You can enable notifications by configuring a script that is run whenever the ag ### `codex exec` to run Codex programmatically/non-interactively To run Codex non-interactively, run `codex exec PROMPT` (you can also pass the prompt via `stdin`) and Codex will work on your task until it decides that it is done and exits. Output is printed to the terminal directly. You can set the `RUST_LOG` environment variable to see more about what's going on. +Use `codex exec --ephemeral ...` to run without persisting session rollout files to disk. ### Experimenting with the Codex Sandbox diff --git a/codex-rs/app-server-protocol/schema/json/ClientRequest.json b/codex-rs/app-server-protocol/schema/json/ClientRequest.json index 39faeb1378..0ccf453020 100644 --- a/codex-rs/app-server-protocol/schema/json/ClientRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ClientRequest.json @@ -422,6 +422,27 @@ ], "type": "object" }, + "ExperimentalFeatureListParams": { + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a reasonable server-side value.", + "format": "uint32", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "type": "object" + }, "FeedbackUploadParams": { "properties": { "classification": { @@ -480,6 +501,19 @@ }, "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -526,19 +560,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/FunctionCallOutputBody" }, "success": { "type": [ @@ -548,7 +573,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, @@ -1171,6 +1196,7 @@ }, "Personality": { "enum": [ + "none", "friendly", "pragmatic" ], @@ -2235,6 +2261,17 @@ ], "type": "object" }, + "ThreadCompactStartParams": { + "properties": { + "threadId": { + "type": "string" + } + }, + "required": [ + "threadId" + ], + "type": "object" + }, "ThreadForkParams": { "description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.", "properties": { @@ -2773,6 +2810,29 @@ ], "type": "object" }, + "TurnSteerParams": { + "properties": { + "expectedTurnId": { + "description": "Required active turn id precondition. The request fails when it does not match the currently active turn.", + "type": "string" + }, + "input": { + "items": { + "$ref": "#/definitions/UserInput" + }, + "type": "array" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "expectedTurnId", + "input", + "threadId" + ], + "type": "object" + }, "UserInput": { "oneOf": [ { @@ -3210,6 +3270,30 @@ "title": "Thread/unarchiveRequest", "type": "object" }, + { + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "enum": [ + "thread/compact/start" + ], + "title": "Thread/compact/startRequestMethod", + "type": "string" + }, + "params": { + "$ref": "#/definitions/ThreadCompactStartParams" + } + }, + "required": [ + "id", + "method", + "params" + ], + "title": "Thread/compact/startRequest", + "type": "object" + }, { "properties": { "id": { @@ -3450,6 +3534,30 @@ "title": "Turn/startRequest", "type": "object" }, + { + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "enum": [ + "turn/steer" + ], + "title": "Turn/steerRequestMethod", + "type": "string" + }, + "params": { + "$ref": "#/definitions/TurnSteerParams" + } + }, + "required": [ + "id", + "method", + "params" + ], + "title": "Turn/steerRequest", + "type": "object" + }, { "properties": { "id": { @@ -3522,6 +3630,30 @@ "title": "Model/listRequest", "type": "object" }, + { + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "enum": [ + "experimentalFeature/list" + ], + "title": "ExperimentalFeature/listRequestMethod", + "type": "string" + }, + "params": { + "$ref": "#/definitions/ExperimentalFeatureListParams" + } + }, + "required": [ + "id", + "method", + "params" + ], + "title": "ExperimentalFeature/listRequest", + "type": "object" + }, { "properties": { "id": { diff --git a/codex-rs/app-server-protocol/schema/json/DynamicToolCallResponse.json b/codex-rs/app-server-protocol/schema/json/DynamicToolCallResponse.json index 662d3bda4f..e0e29641d2 100644 --- a/codex-rs/app-server-protocol/schema/json/DynamicToolCallResponse.json +++ b/codex-rs/app-server-protocol/schema/json/DynamicToolCallResponse.json @@ -1,15 +1,64 @@ { "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "DynamicToolCallOutputContentItem": { + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "inputText" + ], + "title": "InputTextDynamicToolCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextDynamicToolCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "imageUrl": { + "type": "string" + }, + "type": { + "enum": [ + "inputImage" + ], + "title": "InputImageDynamicToolCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "imageUrl", + "type" + ], + "title": "InputImageDynamicToolCallOutputContentItem", + "type": "object" + } + ] + } + }, "properties": { - "output": { - "type": "string" + "contentItems": { + "items": { + "$ref": "#/definitions/DynamicToolCallOutputContentItem" + }, + "type": "array" }, "success": { "type": "boolean" } }, "required": [ - "output", + "contentItems", "success" ], "title": "DynamicToolCallResponse", diff --git a/codex-rs/app-server-protocol/schema/json/EventMsg.json b/codex-rs/app-server-protocol/schema/json/EventMsg.json index ab556a77cf..f399912e03 100644 --- a/codex-rs/app-server-protocol/schema/json/EventMsg.json +++ b/codex-rs/app-server-protocol/schema/json/EventMsg.json @@ -2864,6 +2864,19 @@ } ] }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -2910,19 +2923,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/FunctionCallOutputBody" }, "success": { "type": [ @@ -2932,7 +2936,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, diff --git a/codex-rs/app-server-protocol/schema/json/ServerNotification.json b/codex-rs/app-server-protocol/schema/json/ServerNotification.json index f6d10c11c2..0e2f94f4d8 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerNotification.json +++ b/codex-rs/app-server-protocol/schema/json/ServerNotification.json @@ -3484,6 +3484,19 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -3530,19 +3543,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/FunctionCallOutputBody" }, "success": { "type": [ @@ -3552,7 +3556,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index 83a7c781b0..3b6e61fc23 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -598,6 +598,30 @@ "title": "Thread/unarchiveRequest", "type": "object" }, + { + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "enum": [ + "thread/compact/start" + ], + "title": "Thread/compact/startRequestMethod", + "type": "string" + }, + "params": { + "$ref": "#/definitions/v2/ThreadCompactStartParams" + } + }, + "required": [ + "id", + "method", + "params" + ], + "title": "Thread/compact/startRequest", + "type": "object" + }, { "properties": { "id": { @@ -838,6 +862,30 @@ "title": "Turn/startRequest", "type": "object" }, + { + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "enum": [ + "turn/steer" + ], + "title": "Turn/steerRequestMethod", + "type": "string" + }, + "params": { + "$ref": "#/definitions/v2/TurnSteerParams" + } + }, + "required": [ + "id", + "method", + "params" + ], + "title": "Turn/steerRequest", + "type": "object" + }, { "properties": { "id": { @@ -910,6 +958,30 @@ "title": "Model/listRequest", "type": "object" }, + { + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "enum": [ + "experimentalFeature/list" + ], + "title": "ExperimentalFeature/listRequestMethod", + "type": "string" + }, + "params": { + "$ref": "#/definitions/v2/ExperimentalFeatureListParams" + } + }, + "required": [ + "id", + "method", + "params" + ], + "title": "ExperimentalFeature/listRequest", + "type": "object" + }, { "properties": { "id": { @@ -2288,6 +2360,50 @@ ], "type": "object" }, + "DynamicToolCallOutputContentItem": { + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "inputText" + ], + "title": "InputTextDynamicToolCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextDynamicToolCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "imageUrl": { + "type": "string" + }, + "type": { + "enum": [ + "inputImage" + ], + "title": "InputImageDynamicToolCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "imageUrl", + "type" + ], + "title": "InputImageDynamicToolCallOutputContentItem", + "type": "object" + } + ] + }, "DynamicToolCallParams": { "$schema": "http://json-schema.org/draft-07/schema#", "properties": { @@ -2318,15 +2434,18 @@ "DynamicToolCallResponse": { "$schema": "http://json-schema.org/draft-07/schema#", "properties": { - "output": { - "type": "string" + "contentItems": { + "items": { + "$ref": "#/definitions/DynamicToolCallOutputContentItem" + }, + "type": "array" }, "success": { "type": "boolean" } }, "required": [ - "output", + "contentItems", "success" ], "title": "DynamicToolCallResponse", @@ -5020,6 +5139,19 @@ "title": "ForkConversationResponse", "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -5066,19 +5198,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/FunctionCallOutputBody" }, "success": { "type": [ @@ -5088,7 +5211,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, @@ -11129,6 +11252,143 @@ "title": "ErrorNotification", "type": "object" }, + "ExperimentalFeature": { + "properties": { + "announcement": { + "description": "Announcement copy shown to users when the feature is introduced. Null when this feature is not in beta.", + "type": [ + "string", + "null" + ] + }, + "defaultEnabled": { + "description": "Whether this feature is enabled by default.", + "type": "boolean" + }, + "description": { + "description": "Short summary describing what the feature does. Null when this feature is not in beta.", + "type": [ + "string", + "null" + ] + }, + "displayName": { + "description": "User-facing display name shown in the experimental features UI. Null when this feature is not in beta.", + "type": [ + "string", + "null" + ] + }, + "enabled": { + "description": "Whether this feature is currently enabled in the loaded config.", + "type": "boolean" + }, + "name": { + "description": "Stable key used in config.toml and CLI flag toggles.", + "type": "string" + }, + "stage": { + "allOf": [ + { + "$ref": "#/definitions/v2/ExperimentalFeatureStage" + } + ], + "description": "Lifecycle stage of this feature flag." + } + }, + "required": [ + "defaultEnabled", + "enabled", + "name", + "stage" + ], + "type": "object" + }, + "ExperimentalFeatureListParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a reasonable server-side value.", + "format": "uint32", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "title": "ExperimentalFeatureListParams", + "type": "object" + }, + "ExperimentalFeatureListResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "data": { + "items": { + "$ref": "#/definitions/v2/ExperimentalFeature" + }, + "type": "array" + }, + "nextCursor": { + "description": "Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "data" + ], + "title": "ExperimentalFeatureListResponse", + "type": "object" + }, + "ExperimentalFeatureStage": { + "oneOf": [ + { + "description": "Feature is available for user testing and feedback.", + "enum": [ + "beta" + ], + "type": "string" + }, + { + "description": "Feature is still being built and not ready for broad use.", + "enum": [ + "underDevelopment" + ], + "type": "string" + }, + { + "description": "Feature is production-ready.", + "enum": [ + "stable" + ], + "type": "string" + }, + { + "description": "Feature is deprecated and should be avoided.", + "enum": [ + "deprecated" + ], + "type": "string" + }, + { + "description": "Feature flag is retained only for backwards compatibility.", + "enum": [ + "removed" + ], + "type": "string" + } + ] + }, "FeedbackUploadParams": { "$schema": "http://json-schema.org/draft-07/schema#", "properties": { @@ -11222,6 +11482,19 @@ ], "type": "string" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/v2/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -11268,19 +11541,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/v2/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/v2/FunctionCallOutputBody" }, "success": { "type": [ @@ -11290,7 +11554,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, @@ -11938,6 +12202,12 @@ "supportsPersonality": { "default": false, "type": "boolean" + }, + "upgrade": { + "type": [ + "string", + "null" + ] } }, "required": [ @@ -12090,6 +12360,7 @@ }, "Personality": { "enum": [ + "none", "friendly", "pragmatic" ], @@ -13849,6 +14120,24 @@ "title": "ThreadArchiveResponse", "type": "object" }, + "ThreadCompactStartParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "threadId": { + "type": "string" + } + }, + "required": [ + "threadId" + ], + "title": "ThreadCompactStartParams", + "type": "object" + }, + "ThreadCompactStartResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadCompactStartResponse", + "type": "object" + }, "ThreadForkParams": { "$schema": "http://json-schema.org/draft-07/schema#", "description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.", @@ -15464,6 +15753,44 @@ ], "type": "string" }, + "TurnSteerParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "expectedTurnId": { + "description": "Required active turn id precondition. The request fails when it does not match the currently active turn.", + "type": "string" + }, + "input": { + "items": { + "$ref": "#/definitions/v2/UserInput" + }, + "type": "array" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "expectedTurnId", + "input", + "threadId" + ], + "title": "TurnSteerParams", + "type": "object" + }, + "TurnSteerResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "turnId": { + "type": "string" + } + }, + "required": [ + "turnId" + ], + "title": "TurnSteerResponse", + "type": "object" + }, "UserInput": { "oneOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/v1/ForkConversationResponse.json b/codex-rs/app-server-protocol/schema/json/v1/ForkConversationResponse.json index 215c80b28d..a5838e89e7 100644 --- a/codex-rs/app-server-protocol/schema/json/v1/ForkConversationResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v1/ForkConversationResponse.json @@ -2864,6 +2864,19 @@ } ] }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -2910,19 +2923,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/FunctionCallOutputBody" }, "success": { "type": [ @@ -2932,7 +2936,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, diff --git a/codex-rs/app-server-protocol/schema/json/v1/ResumeConversationParams.json b/codex-rs/app-server-protocol/schema/json/v1/ResumeConversationParams.json index 1261306e61..9ce52963d9 100644 --- a/codex-rs/app-server-protocol/schema/json/v1/ResumeConversationParams.json +++ b/codex-rs/app-server-protocol/schema/json/v1/ResumeConversationParams.json @@ -98,6 +98,19 @@ } ] }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -144,19 +157,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/FunctionCallOutputBody" }, "success": { "type": [ @@ -166,7 +170,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, diff --git a/codex-rs/app-server-protocol/schema/json/v1/ResumeConversationResponse.json b/codex-rs/app-server-protocol/schema/json/v1/ResumeConversationResponse.json index bd6d80b6ed..718b17aa28 100644 --- a/codex-rs/app-server-protocol/schema/json/v1/ResumeConversationResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v1/ResumeConversationResponse.json @@ -2864,6 +2864,19 @@ } ] }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -2910,19 +2923,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/FunctionCallOutputBody" }, "success": { "type": [ @@ -2932,7 +2936,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, diff --git a/codex-rs/app-server-protocol/schema/json/v1/SessionConfiguredNotification.json b/codex-rs/app-server-protocol/schema/json/v1/SessionConfiguredNotification.json index a61d4141a6..a85b78281b 100644 --- a/codex-rs/app-server-protocol/schema/json/v1/SessionConfiguredNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v1/SessionConfiguredNotification.json @@ -2864,6 +2864,19 @@ } ] }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -2910,19 +2923,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/FunctionCallOutputBody" }, "success": { "type": [ @@ -2932,7 +2936,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ExperimentalFeatureListParams.json b/codex-rs/app-server-protocol/schema/json/v2/ExperimentalFeatureListParams.json new file mode 100644 index 0000000000..ab562edbf2 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/json/v2/ExperimentalFeatureListParams.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a reasonable server-side value.", + "format": "uint32", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "title": "ExperimentalFeatureListParams", + "type": "object" +} \ No newline at end of file diff --git a/codex-rs/app-server-protocol/schema/json/v2/ExperimentalFeatureListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ExperimentalFeatureListResponse.json new file mode 100644 index 0000000000..25398fc0ea --- /dev/null +++ b/codex-rs/app-server-protocol/schema/json/v2/ExperimentalFeatureListResponse.json @@ -0,0 +1,116 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "ExperimentalFeature": { + "properties": { + "announcement": { + "description": "Announcement copy shown to users when the feature is introduced. Null when this feature is not in beta.", + "type": [ + "string", + "null" + ] + }, + "defaultEnabled": { + "description": "Whether this feature is enabled by default.", + "type": "boolean" + }, + "description": { + "description": "Short summary describing what the feature does. Null when this feature is not in beta.", + "type": [ + "string", + "null" + ] + }, + "displayName": { + "description": "User-facing display name shown in the experimental features UI. Null when this feature is not in beta.", + "type": [ + "string", + "null" + ] + }, + "enabled": { + "description": "Whether this feature is currently enabled in the loaded config.", + "type": "boolean" + }, + "name": { + "description": "Stable key used in config.toml and CLI flag toggles.", + "type": "string" + }, + "stage": { + "allOf": [ + { + "$ref": "#/definitions/ExperimentalFeatureStage" + } + ], + "description": "Lifecycle stage of this feature flag." + } + }, + "required": [ + "defaultEnabled", + "enabled", + "name", + "stage" + ], + "type": "object" + }, + "ExperimentalFeatureStage": { + "oneOf": [ + { + "description": "Feature is available for user testing and feedback.", + "enum": [ + "beta" + ], + "type": "string" + }, + { + "description": "Feature is still being built and not ready for broad use.", + "enum": [ + "underDevelopment" + ], + "type": "string" + }, + { + "description": "Feature is production-ready.", + "enum": [ + "stable" + ], + "type": "string" + }, + { + "description": "Feature is deprecated and should be avoided.", + "enum": [ + "deprecated" + ], + "type": "string" + }, + { + "description": "Feature flag is retained only for backwards compatibility.", + "enum": [ + "removed" + ], + "type": "string" + } + ] + } + }, + "properties": { + "data": { + "items": { + "$ref": "#/definitions/ExperimentalFeature" + }, + "type": "array" + }, + "nextCursor": { + "description": "Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "data" + ], + "title": "ExperimentalFeatureListResponse", + "type": "object" +} \ No newline at end of file diff --git a/codex-rs/app-server-protocol/schema/json/v2/ModelListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ModelListResponse.json index 8c8126bb6b..a1b3f7114c 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ModelListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ModelListResponse.json @@ -59,6 +59,12 @@ "supportsPersonality": { "default": false, "type": "boolean" + }, + "upgrade": { + "type": [ + "string", + "null" + ] } }, "required": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/RawResponseItemCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/RawResponseItemCompletedNotification.json index 1b307c9b89..c1e36ad8e8 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/RawResponseItemCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/RawResponseItemCompletedNotification.json @@ -65,6 +65,19 @@ } ] }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -111,19 +124,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/FunctionCallOutputBody" }, "success": { "type": [ @@ -133,7 +137,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadCompactStartParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadCompactStartParams.json new file mode 100644 index 0000000000..a174ff95d9 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadCompactStartParams.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "threadId": { + "type": "string" + } + }, + "required": [ + "threadId" + ], + "title": "ThreadCompactStartParams", + "type": "object" +} \ No newline at end of file diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadCompactStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadCompactStartResponse.json new file mode 100644 index 0000000000..bb372b6ddd --- /dev/null +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadCompactStartResponse.json @@ -0,0 +1,5 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadCompactStartResponse", + "type": "object" +} \ No newline at end of file diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json index cc05de490a..ed1d5274e0 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json @@ -74,6 +74,19 @@ } ] }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, "FunctionCallOutputContentItem": { "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", "oneOf": [ @@ -120,19 +133,10 @@ ] }, "FunctionCallOutputPayload": { - "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses API understands.", + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`body` serializes directly as the wire value for `function_call_output.output`. `success` remains internal metadata for downstream handling.", "properties": { - "content": { - "type": "string" - }, - "content_items": { - "items": { - "$ref": "#/definitions/FunctionCallOutputContentItem" - }, - "type": [ - "array", - "null" - ] + "body": { + "$ref": "#/definitions/FunctionCallOutputBody" }, "success": { "type": [ @@ -142,7 +146,7 @@ } }, "required": [ - "content" + "body" ], "type": "object" }, @@ -251,6 +255,7 @@ }, "Personality": { "enum": [ + "none", "friendly", "pragmatic" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json index 03bed7f93c..f2b0052e6d 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json @@ -29,6 +29,7 @@ }, "Personality": { "enum": [ + "none", "friendly", "pragmatic" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json index 6a739b31ac..be351a48ea 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json @@ -66,6 +66,7 @@ }, "Personality": { "enum": [ + "none", "friendly", "pragmatic" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnSteerParams.json b/codex-rs/app-server-protocol/schema/json/v2/TurnSteerParams.json new file mode 100644 index 0000000000..a064d9e7e3 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnSteerParams.json @@ -0,0 +1,189 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "ByteRange": { + "properties": { + "end": { + "format": "uint", + "minimum": 0.0, + "type": "integer" + }, + "start": { + "format": "uint", + "minimum": 0.0, + "type": "integer" + } + }, + "required": [ + "end", + "start" + ], + "type": "object" + }, + "TextElement": { + "properties": { + "byteRange": { + "allOf": [ + { + "$ref": "#/definitions/ByteRange" + } + ], + "description": "Byte range in the parent `text` buffer that this element occupies." + }, + "placeholder": { + "description": "Optional human-readable placeholder for the element, displayed in the UI.", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "byteRange" + ], + "type": "object" + }, + "UserInput": { + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "text_elements": { + "default": [], + "description": "UI-defined spans within `text` used to render or persist special elements.", + "items": { + "$ref": "#/definitions/TextElement" + }, + "type": "array" + }, + "type": { + "enum": [ + "text" + ], + "title": "TextUserInputType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "TextUserInput", + "type": "object" + }, + { + "properties": { + "type": { + "enum": [ + "image" + ], + "title": "ImageUserInputType", + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "type", + "url" + ], + "title": "ImageUserInput", + "type": "object" + }, + { + "properties": { + "path": { + "type": "string" + }, + "type": { + "enum": [ + "localImage" + ], + "title": "LocalImageUserInputType", + "type": "string" + } + }, + "required": [ + "path", + "type" + ], + "title": "LocalImageUserInput", + "type": "object" + }, + { + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "type": { + "enum": [ + "skill" + ], + "title": "SkillUserInputType", + "type": "string" + } + }, + "required": [ + "name", + "path", + "type" + ], + "title": "SkillUserInput", + "type": "object" + }, + { + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "type": { + "enum": [ + "mention" + ], + "title": "MentionUserInputType", + "type": "string" + } + }, + "required": [ + "name", + "path", + "type" + ], + "title": "MentionUserInput", + "type": "object" + } + ] + } + }, + "properties": { + "expectedTurnId": { + "description": "Required active turn id precondition. The request fails when it does not match the currently active turn.", + "type": "string" + }, + "input": { + "items": { + "$ref": "#/definitions/UserInput" + }, + "type": "array" + }, + "threadId": { + "type": "string" + } + }, + "required": [ + "expectedTurnId", + "input", + "threadId" + ], + "title": "TurnSteerParams", + "type": "object" +} \ No newline at end of file diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnSteerResponse.json b/codex-rs/app-server-protocol/schema/json/v2/TurnSteerResponse.json new file mode 100644 index 0000000000..d801a3613c --- /dev/null +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnSteerResponse.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "turnId": { + "type": "string" + } + }, + "required": [ + "turnId" + ], + "title": "TurnSteerResponse", + "type": "object" +} \ No newline at end of file diff --git a/codex-rs/app-server-protocol/schema/typescript/ClientRequest.ts b/codex-rs/app-server-protocol/schema/typescript/ClientRequest.ts index d526e5c5e9..3b5f6787b1 100644 --- a/codex-rs/app-server-protocol/schema/typescript/ClientRequest.ts +++ b/codex-rs/app-server-protocol/schema/typescript/ClientRequest.ts @@ -27,6 +27,7 @@ import type { CommandExecParams } from "./v2/CommandExecParams"; import type { ConfigBatchWriteParams } from "./v2/ConfigBatchWriteParams"; import type { ConfigReadParams } from "./v2/ConfigReadParams"; import type { ConfigValueWriteParams } from "./v2/ConfigValueWriteParams"; +import type { ExperimentalFeatureListParams } from "./v2/ExperimentalFeatureListParams"; import type { FeedbackUploadParams } from "./v2/FeedbackUploadParams"; import type { GetAccountParams } from "./v2/GetAccountParams"; import type { ListMcpServerStatusParams } from "./v2/ListMcpServerStatusParams"; @@ -39,6 +40,7 @@ import type { SkillsListParams } from "./v2/SkillsListParams"; import type { SkillsRemoteReadParams } from "./v2/SkillsRemoteReadParams"; import type { SkillsRemoteWriteParams } from "./v2/SkillsRemoteWriteParams"; import type { ThreadArchiveParams } from "./v2/ThreadArchiveParams"; +import type { ThreadCompactStartParams } from "./v2/ThreadCompactStartParams"; import type { ThreadForkParams } from "./v2/ThreadForkParams"; import type { ThreadListParams } from "./v2/ThreadListParams"; import type { ThreadLoadedListParams } from "./v2/ThreadLoadedListParams"; @@ -50,8 +52,9 @@ import type { ThreadStartParams } from "./v2/ThreadStartParams"; import type { ThreadUnarchiveParams } from "./v2/ThreadUnarchiveParams"; import type { TurnInterruptParams } from "./v2/TurnInterruptParams"; import type { TurnStartParams } from "./v2/TurnStartParams"; +import type { TurnSteerParams } from "./v2/TurnSteerParams"; /** * Request from the client to the server. */ -export type ClientRequest ={ "method": "initialize", id: RequestId, params: InitializeParams, } | { "method": "thread/start", id: RequestId, params: ThreadStartParams, } | { "method": "thread/resume", id: RequestId, params: ThreadResumeParams, } | { "method": "thread/fork", id: RequestId, params: ThreadForkParams, } | { "method": "thread/archive", id: RequestId, params: ThreadArchiveParams, } | { "method": "thread/name/set", id: RequestId, params: ThreadSetNameParams, } | { "method": "thread/unarchive", id: RequestId, params: ThreadUnarchiveParams, } | { "method": "thread/rollback", id: RequestId, params: ThreadRollbackParams, } | { "method": "thread/list", id: RequestId, params: ThreadListParams, } | { "method": "thread/loaded/list", id: RequestId, params: ThreadLoadedListParams, } | { "method": "thread/read", id: RequestId, params: ThreadReadParams, } | { "method": "skills/list", id: RequestId, params: SkillsListParams, } | { "method": "skills/remote/read", id: RequestId, params: SkillsRemoteReadParams, } | { "method": "skills/remote/write", id: RequestId, params: SkillsRemoteWriteParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "method": "turn/start", id: RequestId, params: TurnStartParams, } | { "method": "turn/interrupt", id: RequestId, params: TurnInterruptParams, } | { "method": "review/start", id: RequestId, params: ReviewStartParams, } | { "method": "model/list", id: RequestId, params: ModelListParams, } | { "method": "mcpServer/oauth/login", id: RequestId, params: McpServerOauthLoginParams, } | { "method": "config/mcpServer/reload", id: RequestId, params: undefined, } | { "method": "mcpServerStatus/list", id: RequestId, params: ListMcpServerStatusParams, } | { "method": "account/login/start", id: RequestId, params: LoginAccountParams, } | { "method": "account/login/cancel", id: RequestId, params: CancelLoginAccountParams, } | { "method": "account/logout", id: RequestId, params: undefined, } | { "method": "account/rateLimits/read", id: RequestId, params: undefined, } | { "method": "feedback/upload", id: RequestId, params: FeedbackUploadParams, } | { "method": "command/exec", id: RequestId, params: CommandExecParams, } | { "method": "config/read", id: RequestId, params: ConfigReadParams, } | { "method": "config/value/write", id: RequestId, params: ConfigValueWriteParams, } | { "method": "config/batchWrite", id: RequestId, params: ConfigBatchWriteParams, } | { "method": "configRequirements/read", id: RequestId, params: undefined, } | { "method": "account/read", id: RequestId, params: GetAccountParams, } | { "method": "newConversation", id: RequestId, params: NewConversationParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "listConversations", id: RequestId, params: ListConversationsParams, } | { "method": "resumeConversation", id: RequestId, params: ResumeConversationParams, } | { "method": "forkConversation", id: RequestId, params: ForkConversationParams, } | { "method": "archiveConversation", id: RequestId, params: ArchiveConversationParams, } | { "method": "sendUserMessage", id: RequestId, params: SendUserMessageParams, } | { "method": "sendUserTurn", id: RequestId, params: SendUserTurnParams, } | { "method": "interruptConversation", id: RequestId, params: InterruptConversationParams, } | { "method": "addConversationListener", id: RequestId, params: AddConversationListenerParams, } | { "method": "removeConversationListener", id: RequestId, params: RemoveConversationListenerParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "loginApiKey", id: RequestId, params: LoginApiKeyParams, } | { "method": "loginChatGpt", id: RequestId, params: undefined, } | { "method": "cancelLoginChatGpt", id: RequestId, params: CancelLoginChatGptParams, } | { "method": "logoutChatGpt", id: RequestId, params: undefined, } | { "method": "getAuthStatus", id: RequestId, params: GetAuthStatusParams, } | { "method": "getUserSavedConfig", id: RequestId, params: undefined, } | { "method": "setDefaultModel", id: RequestId, params: SetDefaultModelParams, } | { "method": "getUserAgent", id: RequestId, params: undefined, } | { "method": "userInfo", id: RequestId, params: undefined, } | { "method": "fuzzyFileSearch", id: RequestId, params: FuzzyFileSearchParams, } | { "method": "execOneOffCommand", id: RequestId, params: ExecOneOffCommandParams, }; +export type ClientRequest ={ "method": "initialize", id: RequestId, params: InitializeParams, } | { "method": "thread/start", id: RequestId, params: ThreadStartParams, } | { "method": "thread/resume", id: RequestId, params: ThreadResumeParams, } | { "method": "thread/fork", id: RequestId, params: ThreadForkParams, } | { "method": "thread/archive", id: RequestId, params: ThreadArchiveParams, } | { "method": "thread/name/set", id: RequestId, params: ThreadSetNameParams, } | { "method": "thread/unarchive", id: RequestId, params: ThreadUnarchiveParams, } | { "method": "thread/compact/start", id: RequestId, params: ThreadCompactStartParams, } | { "method": "thread/rollback", id: RequestId, params: ThreadRollbackParams, } | { "method": "thread/list", id: RequestId, params: ThreadListParams, } | { "method": "thread/loaded/list", id: RequestId, params: ThreadLoadedListParams, } | { "method": "thread/read", id: RequestId, params: ThreadReadParams, } | { "method": "skills/list", id: RequestId, params: SkillsListParams, } | { "method": "skills/remote/read", id: RequestId, params: SkillsRemoteReadParams, } | { "method": "skills/remote/write", id: RequestId, params: SkillsRemoteWriteParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "method": "turn/start", id: RequestId, params: TurnStartParams, } | { "method": "turn/steer", id: RequestId, params: TurnSteerParams, } | { "method": "turn/interrupt", id: RequestId, params: TurnInterruptParams, } | { "method": "review/start", id: RequestId, params: ReviewStartParams, } | { "method": "model/list", id: RequestId, params: ModelListParams, } | { "method": "experimentalFeature/list", id: RequestId, params: ExperimentalFeatureListParams, } | { "method": "mcpServer/oauth/login", id: RequestId, params: McpServerOauthLoginParams, } | { "method": "config/mcpServer/reload", id: RequestId, params: undefined, } | { "method": "mcpServerStatus/list", id: RequestId, params: ListMcpServerStatusParams, } | { "method": "account/login/start", id: RequestId, params: LoginAccountParams, } | { "method": "account/login/cancel", id: RequestId, params: CancelLoginAccountParams, } | { "method": "account/logout", id: RequestId, params: undefined, } | { "method": "account/rateLimits/read", id: RequestId, params: undefined, } | { "method": "feedback/upload", id: RequestId, params: FeedbackUploadParams, } | { "method": "command/exec", id: RequestId, params: CommandExecParams, } | { "method": "config/read", id: RequestId, params: ConfigReadParams, } | { "method": "config/value/write", id: RequestId, params: ConfigValueWriteParams, } | { "method": "config/batchWrite", id: RequestId, params: ConfigBatchWriteParams, } | { "method": "configRequirements/read", id: RequestId, params: undefined, } | { "method": "account/read", id: RequestId, params: GetAccountParams, } | { "method": "newConversation", id: RequestId, params: NewConversationParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "listConversations", id: RequestId, params: ListConversationsParams, } | { "method": "resumeConversation", id: RequestId, params: ResumeConversationParams, } | { "method": "forkConversation", id: RequestId, params: ForkConversationParams, } | { "method": "archiveConversation", id: RequestId, params: ArchiveConversationParams, } | { "method": "sendUserMessage", id: RequestId, params: SendUserMessageParams, } | { "method": "sendUserTurn", id: RequestId, params: SendUserTurnParams, } | { "method": "interruptConversation", id: RequestId, params: InterruptConversationParams, } | { "method": "addConversationListener", id: RequestId, params: AddConversationListenerParams, } | { "method": "removeConversationListener", id: RequestId, params: RemoveConversationListenerParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "loginApiKey", id: RequestId, params: LoginApiKeyParams, } | { "method": "loginChatGpt", id: RequestId, params: undefined, } | { "method": "cancelLoginChatGpt", id: RequestId, params: CancelLoginChatGptParams, } | { "method": "logoutChatGpt", id: RequestId, params: undefined, } | { "method": "getAuthStatus", id: RequestId, params: GetAuthStatusParams, } | { "method": "getUserSavedConfig", id: RequestId, params: undefined, } | { "method": "setDefaultModel", id: RequestId, params: SetDefaultModelParams, } | { "method": "getUserAgent", id: RequestId, params: undefined, } | { "method": "userInfo", id: RequestId, params: undefined, } | { "method": "fuzzyFileSearch", id: RequestId, params: FuzzyFileSearchParams, } | { "method": "execOneOffCommand", id: RequestId, params: ExecOneOffCommandParams, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/FunctionCallOutputBody.ts b/codex-rs/app-server-protocol/schema/typescript/FunctionCallOutputBody.ts new file mode 100644 index 0000000000..6bcb7e25d6 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/FunctionCallOutputBody.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FunctionCallOutputContentItem } from "./FunctionCallOutputContentItem"; + +export type FunctionCallOutputBody = string | Array; diff --git a/codex-rs/app-server-protocol/schema/typescript/FunctionCallOutputPayload.ts b/codex-rs/app-server-protocol/schema/typescript/FunctionCallOutputPayload.ts index 94370f582d..6376c5b8eb 100644 --- a/codex-rs/app-server-protocol/schema/typescript/FunctionCallOutputPayload.ts +++ b/codex-rs/app-server-protocol/schema/typescript/FunctionCallOutputPayload.ts @@ -1,14 +1,12 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { FunctionCallOutputContentItem } from "./FunctionCallOutputContentItem"; +import type { FunctionCallOutputBody } from "./FunctionCallOutputBody"; /** * The payload we send back to OpenAI when reporting a tool call result. * - * `content` preserves the historical plain-string payload so downstream - * integrations (tests, logging, etc.) can keep treating tool output as - * `String`. When an MCP server returns richer data we additionally populate - * `content_items` with the structured form that the Responses API understands. + * `body` serializes directly as the wire value for `function_call_output.output`. + * `success` remains internal metadata for downstream handling. */ -export type FunctionCallOutputPayload = { content: string, content_items: Array | null, success: boolean | null, }; +export type FunctionCallOutputPayload = { body: FunctionCallOutputBody, success: boolean | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/Personality.ts b/codex-rs/app-server-protocol/schema/typescript/Personality.ts index b9ccad4dc2..45165f4e33 100644 --- a/codex-rs/app-server-protocol/schema/typescript/Personality.ts +++ b/codex-rs/app-server-protocol/schema/typescript/Personality.ts @@ -2,4 +2,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type Personality = "friendly" | "pragmatic"; +export type Personality = "none" | "friendly" | "pragmatic"; diff --git a/codex-rs/app-server-protocol/schema/typescript/index.ts b/codex-rs/app-server-protocol/schema/typescript/index.ts index 7d3ecb818e..a6ff6fbaf1 100644 --- a/codex-rs/app-server-protocol/schema/typescript/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/index.ts @@ -69,6 +69,7 @@ export type { FileChange } from "./FileChange"; export type { ForcedLoginMethod } from "./ForcedLoginMethod"; export type { ForkConversationParams } from "./ForkConversationParams"; export type { ForkConversationResponse } from "./ForkConversationResponse"; +export type { FunctionCallOutputBody } from "./FunctionCallOutputBody"; export type { FunctionCallOutputContentItem } from "./FunctionCallOutputContentItem"; export type { FunctionCallOutputPayload } from "./FunctionCallOutputPayload"; export type { FuzzyFileSearchParams } from "./FuzzyFileSearchParams"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/DynamicToolCallOutputContentItem.ts b/codex-rs/app-server-protocol/schema/typescript/v2/DynamicToolCallOutputContentItem.ts new file mode 100644 index 0000000000..8f432109d1 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/DynamicToolCallOutputContentItem.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type DynamicToolCallOutputContentItem = { "type": "inputText", text: string, } | { "type": "inputImage", imageUrl: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/DynamicToolCallResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/DynamicToolCallResponse.ts index a35b9b394a..788e6242dc 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/DynamicToolCallResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/DynamicToolCallResponse.ts @@ -1,5 +1,6 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DynamicToolCallOutputContentItem } from "./DynamicToolCallOutputContentItem"; -export type DynamicToolCallResponse = { output: string, success: boolean, }; +export type DynamicToolCallResponse = { contentItems: Array, success: boolean, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeature.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeature.ts new file mode 100644 index 0000000000..e17ef83138 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeature.ts @@ -0,0 +1,37 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExperimentalFeatureStage } from "./ExperimentalFeatureStage"; + +export type ExperimentalFeature = { +/** + * Stable key used in config.toml and CLI flag toggles. + */ +name: string, +/** + * Lifecycle stage of this feature flag. + */ +stage: ExperimentalFeatureStage, +/** + * User-facing display name shown in the experimental features UI. + * Null when this feature is not in beta. + */ +displayName: string | null, +/** + * Short summary describing what the feature does. + * Null when this feature is not in beta. + */ +description: string | null, +/** + * Announcement copy shown to users when the feature is introduced. + * Null when this feature is not in beta. + */ +announcement: string | null, +/** + * Whether this feature is currently enabled in the loaded config. + */ +enabled: boolean, +/** + * Whether this feature is enabled by default. + */ +defaultEnabled: boolean, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeatureListParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeatureListParams.ts new file mode 100644 index 0000000000..1d4dc84e0d --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeatureListParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExperimentalFeatureListParams = { +/** + * Opaque pagination cursor returned by a previous call. + */ +cursor?: string | null, +/** + * Optional page size; defaults to a reasonable server-side value. + */ +limit?: number | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeatureListResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeatureListResponse.ts new file mode 100644 index 0000000000..46b39ba019 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeatureListResponse.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExperimentalFeature } from "./ExperimentalFeature"; + +export type ExperimentalFeatureListResponse = { data: Array, +/** + * Opaque cursor to pass to the next call to continue after the last item. + * If None, there are no more items to return. + */ +nextCursor: string | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeatureStage.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeatureStage.ts new file mode 100644 index 0000000000..dbd206e05f --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ExperimentalFeatureStage.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ExperimentalFeatureStage = "beta" | "underDevelopment" | "stable" | "deprecated" | "removed"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/Model.ts b/codex-rs/app-server-protocol/schema/typescript/v2/Model.ts index 72daa93edd..7528a8fad3 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/Model.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/Model.ts @@ -5,4 +5,4 @@ import type { InputModality } from "../InputModality"; import type { ReasoningEffort } from "../ReasoningEffort"; import type { ReasoningEffortOption } from "./ReasoningEffortOption"; -export type Model = { id: string, model: string, displayName: string, description: string, supportedReasoningEfforts: Array, defaultReasoningEffort: ReasoningEffort, inputModalities: Array, supportsPersonality: boolean, isDefault: boolean, }; +export type Model = { id: string, model: string, upgrade: string | null, displayName: string, description: string, supportedReasoningEfforts: Array, defaultReasoningEffort: ReasoningEffort, inputModalities: Array, supportsPersonality: boolean, isDefault: boolean, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadCompactStartParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadCompactStartParams.ts new file mode 100644 index 0000000000..a60b2c2812 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadCompactStartParams.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadCompactStartParams = { threadId: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadCompactStartResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadCompactStartResponse.ts new file mode 100644 index 0000000000..3794feb270 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadCompactStartResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ThreadCompactStartResponse = Record; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/TurnSteerParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/TurnSteerParams.ts new file mode 100644 index 0000000000..2c84f195cf --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/TurnSteerParams.ts @@ -0,0 +1,11 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { UserInput } from "./UserInput"; + +export type TurnSteerParams = { threadId: string, input: Array, +/** + * Required active turn id precondition. The request fails when it does not + * match the currently active turn. + */ +expectedTurnId: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/TurnSteerResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/TurnSteerResponse.ts new file mode 100644 index 0000000000..390adb4f59 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/TurnSteerResponse.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TurnSteerResponse = { turnId: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts index 83357ff5c6..65a12daa34 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts @@ -46,11 +46,16 @@ export type { ConfigWriteResponse } from "./ConfigWriteResponse"; export type { ContextCompactedNotification } from "./ContextCompactedNotification"; export type { CreditsSnapshot } from "./CreditsSnapshot"; export type { DeprecationNoticeNotification } from "./DeprecationNoticeNotification"; +export type { DynamicToolCallOutputContentItem } from "./DynamicToolCallOutputContentItem"; export type { DynamicToolCallParams } from "./DynamicToolCallParams"; export type { DynamicToolCallResponse } from "./DynamicToolCallResponse"; export type { DynamicToolSpec } from "./DynamicToolSpec"; export type { ErrorNotification } from "./ErrorNotification"; export type { ExecPolicyAmendment } from "./ExecPolicyAmendment"; +export type { ExperimentalFeature } from "./ExperimentalFeature"; +export type { ExperimentalFeatureListParams } from "./ExperimentalFeatureListParams"; +export type { ExperimentalFeatureListResponse } from "./ExperimentalFeatureListResponse"; +export type { ExperimentalFeatureStage } from "./ExperimentalFeatureStage"; export type { FeedbackUploadParams } from "./FeedbackUploadParams"; export type { FeedbackUploadResponse } from "./FeedbackUploadResponse"; export type { FileChangeApprovalDecision } from "./FileChangeApprovalDecision"; @@ -128,6 +133,8 @@ export type { TextRange } from "./TextRange"; export type { Thread } from "./Thread"; export type { ThreadArchiveParams } from "./ThreadArchiveParams"; export type { ThreadArchiveResponse } from "./ThreadArchiveResponse"; +export type { ThreadCompactStartParams } from "./ThreadCompactStartParams"; +export type { ThreadCompactStartResponse } from "./ThreadCompactStartResponse"; export type { ThreadForkParams } from "./ThreadForkParams"; export type { ThreadForkResponse } from "./ThreadForkResponse"; export type { ThreadItem } from "./ThreadItem"; @@ -173,6 +180,8 @@ export type { TurnStartParams } from "./TurnStartParams"; export type { TurnStartResponse } from "./TurnStartResponse"; export type { TurnStartedNotification } from "./TurnStartedNotification"; export type { TurnStatus } from "./TurnStatus"; +export type { TurnSteerParams } from "./TurnSteerParams"; +export type { TurnSteerResponse } from "./TurnSteerResponse"; export type { UserInput } from "./UserInput"; export type { WebSearchAction } from "./WebSearchAction"; export type { WindowsWorldWritableWarningNotification } from "./WindowsWorldWritableWarningNotification"; diff --git a/codex-rs/app-server-protocol/src/protocol/common.rs b/codex-rs/app-server-protocol/src/protocol/common.rs index bb2510684e..46de0e5817 100644 --- a/codex-rs/app-server-protocol/src/protocol/common.rs +++ b/codex-rs/app-server-protocol/src/protocol/common.rs @@ -208,6 +208,10 @@ client_request_definitions! { params: v2::ThreadUnarchiveParams, response: v2::ThreadUnarchiveResponse, }, + ThreadCompactStart => "thread/compact/start" { + params: v2::ThreadCompactStartParams, + response: v2::ThreadCompactStartResponse, + }, ThreadRollback => "thread/rollback" { params: v2::ThreadRollbackParams, response: v2::ThreadRollbackResponse, @@ -248,6 +252,10 @@ client_request_definitions! { params: v2::TurnStartParams, response: v2::TurnStartResponse, }, + TurnSteer => "turn/steer" { + params: v2::TurnSteerParams, + response: v2::TurnSteerResponse, + }, TurnInterrupt => "turn/interrupt" { params: v2::TurnInterruptParams, response: v2::TurnInterruptResponse, @@ -261,6 +269,10 @@ client_request_definitions! { params: v2::ModelListParams, response: v2::ModelListResponse, }, + ExperimentalFeatureList => "experimentalFeature/list" { + params: v2::ExperimentalFeatureListParams, + response: v2::ExperimentalFeatureListResponse, + }, #[experimental("collaborationMode/list")] /// Lists collaboration mode presets. CollaborationModeList => "collaborationMode/list" { @@ -1083,6 +1095,26 @@ mod tests { Ok(()) } + #[test] + fn serialize_list_experimental_features() -> Result<()> { + let request = ClientRequest::ExperimentalFeatureList { + request_id: RequestId::Integer(8), + params: v2::ExperimentalFeatureListParams::default(), + }; + assert_eq!( + json!({ + "method": "experimentalFeature/list", + "id": 8, + "params": { + "cursor": null, + "limit": null + } + }), + serde_json::to_value(&request)?, + ); + Ok(()) + } + #[test] fn mock_experimental_method_is_marked_experimental() { let request = ClientRequest::MockExperimentalMethod { diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 67e7013a31..a978fec6db 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -998,6 +998,7 @@ pub struct ModelListParams { pub struct Model { pub id: String, pub model: String, + pub upgrade: Option, pub display_name: String, pub description: String, pub supported_reasoning_efforts: Vec, @@ -1042,6 +1043,67 @@ pub struct CollaborationModeListResponse { pub data: Vec, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct ExperimentalFeatureListParams { + /// Opaque pagination cursor returned by a previous call. + #[ts(optional = nullable)] + pub cursor: Option, + /// Optional page size; defaults to a reasonable server-side value. + #[ts(optional = nullable)] + pub limit: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub enum ExperimentalFeatureStage { + /// Feature is available for user testing and feedback. + Beta, + /// Feature is still being built and not ready for broad use. + UnderDevelopment, + /// Feature is production-ready. + Stable, + /// Feature is deprecated and should be avoided. + Deprecated, + /// Feature flag is retained only for backwards compatibility. + Removed, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct ExperimentalFeature { + /// Stable key used in config.toml and CLI flag toggles. + pub name: String, + /// Lifecycle stage of this feature flag. + pub stage: ExperimentalFeatureStage, + /// User-facing display name shown in the experimental features UI. + /// Null when this feature is not in beta. + pub display_name: Option, + /// Short summary describing what the feature does. + /// Null when this feature is not in beta. + pub description: Option, + /// Announcement copy shown to users when the feature is introduced. + /// Null when this feature is not in beta. + pub announcement: Option, + /// Whether this feature is currently enabled in the loaded config. + pub enabled: bool, + /// Whether this feature is enabled by default. + pub default_enabled: bool, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct ExperimentalFeatureListResponse { + pub data: Vec, + /// Opaque cursor to pass to the next call to continue after the last item. + /// If None, there are no more items to return. + pub next_cursor: Option, +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] @@ -1407,6 +1469,18 @@ pub struct ThreadUnarchiveResponse { pub thread: Thread, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct ThreadCompactStartParams { + pub thread_id: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct ThreadCompactStartResponse {} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] @@ -2018,6 +2092,24 @@ pub struct TurnStartResponse { pub turn: Turn, } +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct TurnSteerParams { + pub thread_id: String, + pub input: Vec, + /// Required active turn id precondition. The request fails when it does not + /// match the currently active turn. + pub expected_turn_id: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct TurnSteerResponse { + pub turn_id: String, +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] @@ -2813,10 +2905,34 @@ pub struct DynamicToolCallParams { #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct DynamicToolCallResponse { - pub output: String, + pub content_items: Vec, pub success: bool, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(tag = "type", rename_all = "camelCase")] +#[ts(tag = "type")] +#[ts(export_to = "v2/")] +pub enum DynamicToolCallOutputContentItem { + #[serde(rename_all = "camelCase")] + InputText { text: String }, + #[serde(rename_all = "camelCase")] + InputImage { image_url: String }, +} + +impl From + for codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem +{ + fn from(item: DynamicToolCallOutputContentItem) -> Self { + match item { + DynamicToolCallOutputContentItem::InputText { text } => Self::InputText { text }, + DynamicToolCallOutputContentItem::InputImage { image_url } => { + Self::InputImage { image_url } + } + } + } +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] @@ -3176,4 +3292,61 @@ mod tests { }) ); } + + #[test] + fn dynamic_tool_response_serializes_content_items() { + let value = serde_json::to_value(DynamicToolCallResponse { + content_items: vec![DynamicToolCallOutputContentItem::InputText { + text: "dynamic-ok".to_string(), + }], + success: true, + }) + .unwrap(); + + assert_eq!( + value, + json!({ + "contentItems": [ + { + "type": "inputText", + "text": "dynamic-ok" + } + ], + "success": true, + }) + ); + } + + #[test] + fn dynamic_tool_response_serializes_text_and_image_content_items() { + let value = serde_json::to_value(DynamicToolCallResponse { + content_items: vec![ + DynamicToolCallOutputContentItem::InputText { + text: "dynamic-ok".to_string(), + }, + DynamicToolCallOutputContentItem::InputImage { + image_url: "data:image/png;base64,AAA".to_string(), + }, + ], + success: true, + }) + .unwrap(); + + assert_eq!( + value, + json!({ + "contentItems": [ + { + "type": "inputText", + "text": "dynamic-ok" + }, + { + "type": "inputImage", + "imageUrl": "data:image/png;base64,AAA" + } + ], + "success": true, + }) + ); + } } diff --git a/codex-rs/app-server-test-client/BUILD.bazel b/codex-rs/app-server-test-client/BUILD.bazel index e3610747cd..3a1686a04e 100644 --- a/codex-rs/app-server-test-client/BUILD.bazel +++ b/codex-rs/app-server-test-client/BUILD.bazel @@ -1,6 +1,6 @@ load("//:defs.bzl", "codex_rust_crate") codex_rust_crate( - name = "codex-app-server-test-client", + name = "app-server-test-client", crate_name = "codex_app_server_test_client", ) diff --git a/codex-rs/app-server-test-client/src/lib.rs b/codex-rs/app-server-test-client/src/lib.rs new file mode 100644 index 0000000000..90f6adf572 --- /dev/null +++ b/codex-rs/app-server-test-client/src/lib.rs @@ -0,0 +1,1057 @@ +use std::collections::VecDeque; +use std::fs; +use std::io::BufRead; +use std::io::BufReader; +use std::io::Write; +use std::path::Path; +use std::path::PathBuf; +use std::process::Child; +use std::process::ChildStdin; +use std::process::ChildStdout; +use std::process::Command; +use std::process::Stdio; +use std::thread; +use std::time::Duration; + +use anyhow::Context; +use anyhow::Result; +use anyhow::bail; +use clap::ArgAction; +use clap::Parser; +use clap::Subcommand; +use codex_app_server_protocol::AddConversationListenerParams; +use codex_app_server_protocol::AddConversationSubscriptionResponse; +use codex_app_server_protocol::AskForApproval; +use codex_app_server_protocol::ClientInfo; +use codex_app_server_protocol::ClientRequest; +use codex_app_server_protocol::CommandExecutionApprovalDecision; +use codex_app_server_protocol::CommandExecutionRequestApprovalParams; +use codex_app_server_protocol::CommandExecutionRequestApprovalResponse; +use codex_app_server_protocol::DynamicToolSpec; +use codex_app_server_protocol::FileChangeApprovalDecision; +use codex_app_server_protocol::FileChangeRequestApprovalParams; +use codex_app_server_protocol::FileChangeRequestApprovalResponse; +use codex_app_server_protocol::GetAccountRateLimitsResponse; +use codex_app_server_protocol::InitializeCapabilities; +use codex_app_server_protocol::InitializeParams; +use codex_app_server_protocol::InitializeResponse; +use codex_app_server_protocol::InputItem; +use codex_app_server_protocol::JSONRPCMessage; +use codex_app_server_protocol::JSONRPCNotification; +use codex_app_server_protocol::JSONRPCRequest; +use codex_app_server_protocol::JSONRPCResponse; +use codex_app_server_protocol::LoginChatGptCompleteNotification; +use codex_app_server_protocol::LoginChatGptResponse; +use codex_app_server_protocol::ModelListParams; +use codex_app_server_protocol::ModelListResponse; +use codex_app_server_protocol::NewConversationParams; +use codex_app_server_protocol::NewConversationResponse; +use codex_app_server_protocol::RequestId; +use codex_app_server_protocol::SandboxPolicy; +use codex_app_server_protocol::SendUserMessageParams; +use codex_app_server_protocol::SendUserMessageResponse; +use codex_app_server_protocol::ServerNotification; +use codex_app_server_protocol::ServerRequest; +use codex_app_server_protocol::ThreadStartParams; +use codex_app_server_protocol::ThreadStartResponse; +use codex_app_server_protocol::TurnStartParams; +use codex_app_server_protocol::TurnStartResponse; +use codex_app_server_protocol::TurnStatus; +use codex_app_server_protocol::UserInput as V2UserInput; +use codex_protocol::ThreadId; +use codex_protocol::protocol::Event; +use codex_protocol::protocol::EventMsg; +use serde::Serialize; +use serde::de::DeserializeOwned; +use serde_json::Value; +use uuid::Uuid; + +/// Minimal launcher that initializes the Codex app-server and logs the handshake. +#[derive(Parser)] +#[command(author = "Codex", version, about = "Bootstrap Codex app-server", long_about = None)] +struct Cli { + /// Path to the `codex` CLI binary. + #[arg(long, env = "CODEX_BIN", default_value = "codex")] + codex_bin: PathBuf, + + /// Forwarded to the `codex` CLI as `--config key=value`. Repeatable. + /// + /// Example: + /// `--config 'model_providers.mock.base_url="http://localhost:4010/v2"'` + #[arg( + short = 'c', + long = "config", + value_name = "key=value", + action = ArgAction::Append, + global = true + )] + config_overrides: Vec, + + /// JSON array of dynamic tool specs or a single tool object. + /// Prefix a filename with '@' to read from a file. + /// + /// Example: + /// --dynamic-tools '[{"name":"demo","description":"Demo","inputSchema":{"type":"object"}}]' + /// --dynamic-tools @/path/to/tools.json + #[arg(long, value_name = "json-or-@file", global = true)] + dynamic_tools: Option, + + #[command(subcommand)] + command: CliCommand, +} + +#[derive(Subcommand)] +enum CliCommand { + /// Send a user message through the Codex app-server. + SendMessage { + /// User message to send to Codex. + user_message: String, + }, + /// Send a user message through the app-server V2 thread/turn APIs. + SendMessageV2 { + /// User message to send to Codex. + user_message: String, + }, + /// Start a V2 turn that elicits an ExecCommand approval. + #[command(name = "trigger-cmd-approval")] + TriggerCmdApproval { + /// Optional prompt; defaults to a simple python command. + user_message: Option, + }, + /// Start a V2 turn that elicits an ApplyPatch approval. + #[command(name = "trigger-patch-approval")] + TriggerPatchApproval { + /// Optional prompt; defaults to creating a file via apply_patch. + user_message: Option, + }, + /// Start a V2 turn that should not elicit an ExecCommand approval. + #[command(name = "no-trigger-cmd-approval")] + NoTriggerCmdApproval, + /// Send two sequential V2 turns in the same thread to test follow-up behavior. + SendFollowUpV2 { + /// Initial user message for the first turn. + first_message: String, + /// Follow-up user message for the second turn. + follow_up_message: String, + }, + /// Trigger the ChatGPT login flow and wait for completion. + TestLogin, + /// Fetch the current account rate limits from the Codex app-server. + GetAccountRateLimits, + /// List the available models from the Codex app-server. + #[command(name = "model-list")] + ModelList, +} + +pub fn run() -> Result<()> { + let Cli { + codex_bin, + config_overrides, + dynamic_tools, + command, + } = Cli::parse(); + + let dynamic_tools = parse_dynamic_tools_arg(&dynamic_tools)?; + + match command { + CliCommand::SendMessage { user_message } => { + ensure_dynamic_tools_unused(&dynamic_tools, "send-message")?; + send_message(&codex_bin, &config_overrides, user_message) + } + CliCommand::SendMessageV2 { user_message } => { + send_message_v2(&codex_bin, &config_overrides, user_message, &dynamic_tools) + } + CliCommand::TriggerCmdApproval { user_message } => { + trigger_cmd_approval(&codex_bin, &config_overrides, user_message, &dynamic_tools) + } + CliCommand::TriggerPatchApproval { user_message } => { + trigger_patch_approval(&codex_bin, &config_overrides, user_message, &dynamic_tools) + } + CliCommand::NoTriggerCmdApproval => { + no_trigger_cmd_approval(&codex_bin, &config_overrides, &dynamic_tools) + } + CliCommand::SendFollowUpV2 { + first_message, + follow_up_message, + } => send_follow_up_v2( + &codex_bin, + &config_overrides, + first_message, + follow_up_message, + &dynamic_tools, + ), + CliCommand::TestLogin => { + ensure_dynamic_tools_unused(&dynamic_tools, "test-login")?; + test_login(&codex_bin, &config_overrides) + } + CliCommand::GetAccountRateLimits => { + ensure_dynamic_tools_unused(&dynamic_tools, "get-account-rate-limits")?; + get_account_rate_limits(&codex_bin, &config_overrides) + } + CliCommand::ModelList => { + ensure_dynamic_tools_unused(&dynamic_tools, "model-list")?; + model_list(&codex_bin, &config_overrides) + } + } +} + +fn send_message(codex_bin: &Path, config_overrides: &[String], user_message: String) -> Result<()> { + let mut client = CodexClient::spawn(codex_bin, config_overrides)?; + + let initialize = client.initialize()?; + println!("< initialize response: {initialize:?}"); + + let conversation = client.start_thread()?; + println!("< newConversation response: {conversation:?}"); + + let subscription = client.add_conversation_listener(&conversation.conversation_id)?; + println!("< addConversationListener response: {subscription:?}"); + + let send_response = client.send_user_message(&conversation.conversation_id, &user_message)?; + println!("< sendUserMessage response: {send_response:?}"); + + client.stream_conversation(&conversation.conversation_id)?; + + client.remove_thread_listener(subscription.subscription_id)?; + + Ok(()) +} + +pub fn send_message_v2( + codex_bin: &Path, + config_overrides: &[String], + user_message: String, + dynamic_tools: &Option>, +) -> Result<()> { + send_message_v2_with_policies( + codex_bin, + config_overrides, + user_message, + None, + None, + dynamic_tools, + ) +} + +fn trigger_cmd_approval( + codex_bin: &Path, + config_overrides: &[String], + user_message: Option, + dynamic_tools: &Option>, +) -> Result<()> { + let default_prompt = + "Run `touch /tmp/should-trigger-approval` so I can confirm the file exists."; + let message = user_message.unwrap_or_else(|| default_prompt.to_string()); + send_message_v2_with_policies( + codex_bin, + config_overrides, + message, + Some(AskForApproval::OnRequest), + Some(SandboxPolicy::ReadOnly), + dynamic_tools, + ) +} + +fn trigger_patch_approval( + codex_bin: &Path, + config_overrides: &[String], + user_message: Option, + dynamic_tools: &Option>, +) -> Result<()> { + let default_prompt = + "Create a file named APPROVAL_DEMO.txt containing a short hello message using apply_patch."; + let message = user_message.unwrap_or_else(|| default_prompt.to_string()); + send_message_v2_with_policies( + codex_bin, + config_overrides, + message, + Some(AskForApproval::OnRequest), + Some(SandboxPolicy::ReadOnly), + dynamic_tools, + ) +} + +fn no_trigger_cmd_approval( + codex_bin: &Path, + config_overrides: &[String], + dynamic_tools: &Option>, +) -> Result<()> { + let prompt = "Run `touch should_not_trigger_approval.txt`"; + send_message_v2_with_policies( + codex_bin, + config_overrides, + prompt.to_string(), + None, + None, + dynamic_tools, + ) +} + +fn send_message_v2_with_policies( + codex_bin: &Path, + config_overrides: &[String], + user_message: String, + approval_policy: Option, + sandbox_policy: Option, + dynamic_tools: &Option>, +) -> Result<()> { + let mut client = CodexClient::spawn(codex_bin, config_overrides)?; + + let initialize = client.initialize()?; + println!("< initialize response: {initialize:?}"); + + let thread_response = client.thread_start(ThreadStartParams { + dynamic_tools: dynamic_tools.clone(), + ..Default::default() + })?; + println!("< thread/start response: {thread_response:?}"); + let mut turn_params = TurnStartParams { + thread_id: thread_response.thread.id.clone(), + input: vec![V2UserInput::Text { + text: user_message, + // Test client sends plain text without UI element ranges. + text_elements: Vec::new(), + }], + ..Default::default() + }; + turn_params.approval_policy = approval_policy; + turn_params.sandbox_policy = sandbox_policy; + + let turn_response = client.turn_start(turn_params)?; + println!("< turn/start response: {turn_response:?}"); + + client.stream_turn(&thread_response.thread.id, &turn_response.turn.id)?; + + Ok(()) +} + +fn send_follow_up_v2( + codex_bin: &Path, + config_overrides: &[String], + first_message: String, + follow_up_message: String, + dynamic_tools: &Option>, +) -> Result<()> { + let mut client = CodexClient::spawn(codex_bin, config_overrides)?; + + let initialize = client.initialize()?; + println!("< initialize response: {initialize:?}"); + + let thread_response = client.thread_start(ThreadStartParams { + dynamic_tools: dynamic_tools.clone(), + ..Default::default() + })?; + println!("< thread/start response: {thread_response:?}"); + + let first_turn_params = TurnStartParams { + thread_id: thread_response.thread.id.clone(), + input: vec![V2UserInput::Text { + text: first_message, + // Test client sends plain text without UI element ranges. + text_elements: Vec::new(), + }], + ..Default::default() + }; + let first_turn_response = client.turn_start(first_turn_params)?; + println!("< turn/start response (initial): {first_turn_response:?}"); + client.stream_turn(&thread_response.thread.id, &first_turn_response.turn.id)?; + + let follow_up_params = TurnStartParams { + thread_id: thread_response.thread.id.clone(), + input: vec![V2UserInput::Text { + text: follow_up_message, + // Test client sends plain text without UI element ranges. + text_elements: Vec::new(), + }], + ..Default::default() + }; + let follow_up_response = client.turn_start(follow_up_params)?; + println!("< turn/start response (follow-up): {follow_up_response:?}"); + client.stream_turn(&thread_response.thread.id, &follow_up_response.turn.id)?; + + Ok(()) +} + +fn test_login(codex_bin: &Path, config_overrides: &[String]) -> Result<()> { + let mut client = CodexClient::spawn(codex_bin, config_overrides)?; + + let initialize = client.initialize()?; + println!("< initialize response: {initialize:?}"); + + let login_response = client.login_chat_gpt()?; + println!("< loginChatGpt response: {login_response:?}"); + println!( + "Open the following URL in your browser to continue:\n{}", + login_response.auth_url + ); + + let completion = client.wait_for_login_completion(&login_response.login_id)?; + println!("< loginChatGptComplete notification: {completion:?}"); + + if completion.success { + println!("Login succeeded."); + Ok(()) + } else { + bail!( + "login failed: {}", + completion + .error + .as_deref() + .unwrap_or("unknown error from loginChatGptComplete") + ); + } +} + +fn get_account_rate_limits(codex_bin: &Path, config_overrides: &[String]) -> Result<()> { + let mut client = CodexClient::spawn(codex_bin, config_overrides)?; + + let initialize = client.initialize()?; + println!("< initialize response: {initialize:?}"); + + let response = client.get_account_rate_limits()?; + println!("< account/rateLimits/read response: {response:?}"); + + Ok(()) +} + +fn model_list(codex_bin: &Path, config_overrides: &[String]) -> Result<()> { + let mut client = CodexClient::spawn(codex_bin, config_overrides)?; + + let initialize = client.initialize()?; + println!("< initialize response: {initialize:?}"); + + let response = client.model_list(ModelListParams::default())?; + println!("< model/list response: {response:?}"); + + Ok(()) +} + +fn ensure_dynamic_tools_unused( + dynamic_tools: &Option>, + command: &str, +) -> Result<()> { + if dynamic_tools.is_some() { + bail!( + "dynamic tools are only supported for v2 thread/start; remove --dynamic-tools for {command} or use send-message-v2" + ); + } + Ok(()) +} + +fn parse_dynamic_tools_arg(dynamic_tools: &Option) -> Result>> { + let Some(raw_arg) = dynamic_tools.as_deref() else { + return Ok(None); + }; + + let raw_json = if let Some(path) = raw_arg.strip_prefix('@') { + fs::read_to_string(Path::new(path)) + .with_context(|| format!("read dynamic tools file {path}"))? + } else { + raw_arg.to_string() + }; + + let value: Value = serde_json::from_str(&raw_json).context("parse dynamic tools JSON")?; + let tools = match value { + Value::Array(_) => serde_json::from_value(value).context("decode dynamic tools array")?, + Value::Object(_) => vec![serde_json::from_value(value).context("decode dynamic tool")?], + _ => bail!("dynamic tools JSON must be an object or array"), + }; + + Ok(Some(tools)) +} + +struct CodexClient { + child: Child, + stdin: Option, + stdout: BufReader, + pending_notifications: VecDeque, +} + +impl CodexClient { + fn spawn(codex_bin: &Path, config_overrides: &[String]) -> Result { + let codex_bin_display = codex_bin.display(); + let mut cmd = Command::new(codex_bin); + for override_kv in config_overrides { + cmd.arg("--config").arg(override_kv); + } + let mut codex_app_server = cmd + .arg("app-server") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .with_context(|| format!("failed to start `{codex_bin_display}` app-server"))?; + + let stdin = codex_app_server + .stdin + .take() + .context("codex app-server stdin unavailable")?; + let stdout = codex_app_server + .stdout + .take() + .context("codex app-server stdout unavailable")?; + + Ok(Self { + child: codex_app_server, + stdin: Some(stdin), + stdout: BufReader::new(stdout), + pending_notifications: VecDeque::new(), + }) + } + + fn initialize(&mut self) -> Result { + let request_id = self.request_id(); + let request = ClientRequest::Initialize { + request_id: request_id.clone(), + params: InitializeParams { + client_info: ClientInfo { + name: "codex-toy-app-server".to_string(), + title: Some("Codex Toy App Server".to_string()), + version: env!("CARGO_PKG_VERSION").to_string(), + }, + capabilities: Some(InitializeCapabilities { + experimental_api: true, + }), + }, + }; + + self.send_request(request, request_id, "initialize") + } + + fn start_thread(&mut self) -> Result { + let request_id = self.request_id(); + let request = ClientRequest::NewConversation { + request_id: request_id.clone(), + params: NewConversationParams::default(), + }; + + self.send_request(request, request_id, "newConversation") + } + + fn add_conversation_listener( + &mut self, + conversation_id: &ThreadId, + ) -> Result { + let request_id = self.request_id(); + let request = ClientRequest::AddConversationListener { + request_id: request_id.clone(), + params: AddConversationListenerParams { + conversation_id: *conversation_id, + experimental_raw_events: false, + }, + }; + + self.send_request(request, request_id, "addConversationListener") + } + + fn remove_thread_listener(&mut self, subscription_id: Uuid) -> Result<()> { + let request_id = self.request_id(); + let request = ClientRequest::RemoveConversationListener { + request_id: request_id.clone(), + params: codex_app_server_protocol::RemoveConversationListenerParams { subscription_id }, + }; + + self.send_request::( + request, + request_id, + "removeConversationListener", + )?; + + Ok(()) + } + + fn send_user_message( + &mut self, + conversation_id: &ThreadId, + message: &str, + ) -> Result { + let request_id = self.request_id(); + let request = ClientRequest::SendUserMessage { + request_id: request_id.clone(), + params: SendUserMessageParams { + conversation_id: *conversation_id, + items: vec![InputItem::Text { + text: message.to_string(), + // Test client sends plain text without UI element ranges. + text_elements: Vec::new(), + }], + }, + }; + + self.send_request(request, request_id, "sendUserMessage") + } + + fn thread_start(&mut self, params: ThreadStartParams) -> Result { + let request_id = self.request_id(); + let request = ClientRequest::ThreadStart { + request_id: request_id.clone(), + params, + }; + + self.send_request(request, request_id, "thread/start") + } + + fn turn_start(&mut self, params: TurnStartParams) -> Result { + let request_id = self.request_id(); + let request = ClientRequest::TurnStart { + request_id: request_id.clone(), + params, + }; + + self.send_request(request, request_id, "turn/start") + } + + fn login_chat_gpt(&mut self) -> Result { + let request_id = self.request_id(); + let request = ClientRequest::LoginChatGpt { + request_id: request_id.clone(), + params: None, + }; + + self.send_request(request, request_id, "loginChatGpt") + } + + fn get_account_rate_limits(&mut self) -> Result { + let request_id = self.request_id(); + let request = ClientRequest::GetAccountRateLimits { + request_id: request_id.clone(), + params: None, + }; + + self.send_request(request, request_id, "account/rateLimits/read") + } + + fn model_list(&mut self, params: ModelListParams) -> Result { + let request_id = self.request_id(); + let request = ClientRequest::ModelList { + request_id: request_id.clone(), + params, + }; + + self.send_request(request, request_id, "model/list") + } + + fn stream_conversation(&mut self, conversation_id: &ThreadId) -> Result<()> { + loop { + let notification = self.next_notification()?; + + if !notification.method.starts_with("codex/event/") { + continue; + } + + if let Some(event) = self.extract_event(notification, conversation_id)? { + match &event.msg { + EventMsg::AgentMessage(event) => { + println!("{}", event.message); + } + EventMsg::AgentMessageDelta(event) => { + print!("{}", event.delta); + std::io::stdout().flush().ok(); + } + EventMsg::TurnComplete(event) => { + println!("\n[task complete: {event:?}]"); + break; + } + EventMsg::TurnAborted(event) => { + println!("\n[turn aborted: {:?}]", event.reason); + break; + } + EventMsg::Error(event) => { + println!("[error] {event:?}"); + } + _ => { + println!("[UNKNOWN EVENT] {:?}", event.msg); + } + } + } + } + + Ok(()) + } + + fn wait_for_login_completion( + &mut self, + expected_login_id: &Uuid, + ) -> Result { + loop { + let notification = self.next_notification()?; + + if let Ok(server_notification) = ServerNotification::try_from(notification) { + match server_notification { + ServerNotification::LoginChatGptComplete(completion) => { + if &completion.login_id == expected_login_id { + return Ok(completion); + } + + println!( + "[ignoring loginChatGptComplete for unexpected login_id: {}]", + completion.login_id + ); + } + ServerNotification::AuthStatusChange(status) => { + println!("< authStatusChange notification: {status:?}"); + } + ServerNotification::AccountRateLimitsUpdated(snapshot) => { + println!("< accountRateLimitsUpdated notification: {snapshot:?}"); + } + ServerNotification::SessionConfigured(_) => { + // SessionConfigured notifications are unrelated to login; skip. + } + _ => {} + } + } + + // Not a server notification (likely a conversation event); keep waiting. + } + } + + fn stream_turn(&mut self, thread_id: &str, turn_id: &str) -> Result<()> { + loop { + let notification = self.next_notification()?; + + let Ok(server_notification) = ServerNotification::try_from(notification) else { + continue; + }; + + match server_notification { + ServerNotification::ThreadStarted(payload) => { + if payload.thread.id == thread_id { + println!("< thread/started notification: {:?}", payload.thread); + } + } + ServerNotification::TurnStarted(payload) => { + if payload.turn.id == turn_id { + println!("< turn/started notification: {:?}", payload.turn.status); + } + } + ServerNotification::AgentMessageDelta(delta) => { + print!("{}", delta.delta); + std::io::stdout().flush().ok(); + } + ServerNotification::CommandExecutionOutputDelta(delta) => { + print!("{}", delta.delta); + std::io::stdout().flush().ok(); + } + ServerNotification::TerminalInteraction(delta) => { + println!("[stdin sent: {}]", delta.stdin); + std::io::stdout().flush().ok(); + } + ServerNotification::ItemStarted(payload) => { + println!("\n< item started: {:?}", payload.item); + } + ServerNotification::ItemCompleted(payload) => { + println!("< item completed: {:?}", payload.item); + } + ServerNotification::TurnCompleted(payload) => { + if payload.turn.id == turn_id { + println!("\n< turn/completed notification: {:?}", payload.turn.status); + if payload.turn.status == TurnStatus::Failed + && let Some(error) = payload.turn.error + { + println!("[turn error] {}", error.message); + } + break; + } + } + ServerNotification::McpToolCallProgress(payload) => { + println!("< MCP tool progress: {}", payload.message); + } + _ => { + println!("[UNKNOWN SERVER NOTIFICATION] {server_notification:?}"); + } + } + } + + Ok(()) + } + + fn extract_event( + &self, + notification: JSONRPCNotification, + conversation_id: &ThreadId, + ) -> Result> { + let params = notification + .params + .context("event notification missing params")?; + + let mut map = match params { + Value::Object(map) => map, + other => bail!("unexpected params shape: {other:?}"), + }; + + let conversation_value = map + .remove("conversationId") + .context("event missing conversationId")?; + let notification_conversation: ThreadId = serde_json::from_value(conversation_value) + .context("conversationId was not a valid UUID")?; + + if ¬ification_conversation != conversation_id { + return Ok(None); + } + + let event_value = Value::Object(map); + let event: Event = + serde_json::from_value(event_value).context("failed to decode event payload")?; + Ok(Some(event)) + } + + fn send_request( + &mut self, + request: ClientRequest, + request_id: RequestId, + method: &str, + ) -> Result + where + T: DeserializeOwned, + { + self.write_request(&request)?; + self.wait_for_response(request_id, method) + } + + fn write_request(&mut self, request: &ClientRequest) -> Result<()> { + let request_json = serde_json::to_string(request)?; + let request_pretty = serde_json::to_string_pretty(request)?; + print_multiline_with_prefix("> ", &request_pretty); + + if let Some(stdin) = self.stdin.as_mut() { + writeln!(stdin, "{request_json}")?; + stdin + .flush() + .context("failed to flush request to codex app-server")?; + } else { + bail!("codex app-server stdin closed"); + } + + Ok(()) + } + + fn wait_for_response(&mut self, request_id: RequestId, method: &str) -> Result + where + T: DeserializeOwned, + { + loop { + let message = self.read_jsonrpc_message()?; + + match message { + JSONRPCMessage::Response(JSONRPCResponse { id, result }) => { + if id == request_id { + return serde_json::from_value(result) + .with_context(|| format!("{method} response missing payload")); + } + } + JSONRPCMessage::Error(err) => { + if err.id == request_id { + bail!("{method} failed: {err:?}"); + } + } + JSONRPCMessage::Notification(notification) => { + self.pending_notifications.push_back(notification); + } + JSONRPCMessage::Request(request) => { + self.handle_server_request(request)?; + } + } + } + } + + fn next_notification(&mut self) -> Result { + if let Some(notification) = self.pending_notifications.pop_front() { + return Ok(notification); + } + + loop { + let message = self.read_jsonrpc_message()?; + + match message { + JSONRPCMessage::Notification(notification) => return Ok(notification), + JSONRPCMessage::Response(_) | JSONRPCMessage::Error(_) => { + // No outstanding requests, so ignore stray responses/errors for now. + continue; + } + JSONRPCMessage::Request(request) => { + self.handle_server_request(request)?; + } + } + } + } + + fn read_jsonrpc_message(&mut self) -> Result { + loop { + let mut response_line = String::new(); + let bytes = self + .stdout + .read_line(&mut response_line) + .context("failed to read from codex app-server")?; + + if bytes == 0 { + bail!("codex app-server closed stdout"); + } + + let trimmed = response_line.trim(); + if trimmed.is_empty() { + continue; + } + + let parsed: Value = + serde_json::from_str(trimmed).context("response was not valid JSON-RPC")?; + let pretty = serde_json::to_string_pretty(&parsed)?; + print_multiline_with_prefix("< ", &pretty); + let message: JSONRPCMessage = serde_json::from_value(parsed) + .context("response was not a valid JSON-RPC message")?; + return Ok(message); + } + } + + fn request_id(&self) -> RequestId { + RequestId::String(Uuid::new_v4().to_string()) + } + + fn handle_server_request(&mut self, request: JSONRPCRequest) -> Result<()> { + let server_request = ServerRequest::try_from(request) + .context("failed to deserialize ServerRequest from JSONRPCRequest")?; + + match server_request { + ServerRequest::CommandExecutionRequestApproval { request_id, params } => { + self.handle_command_execution_request_approval(request_id, params)?; + } + ServerRequest::FileChangeRequestApproval { request_id, params } => { + self.approve_file_change_request(request_id, params)?; + } + other => { + bail!("received unsupported server request: {other:?}"); + } + } + + Ok(()) + } + + fn handle_command_execution_request_approval( + &mut self, + request_id: RequestId, + params: CommandExecutionRequestApprovalParams, + ) -> Result<()> { + let CommandExecutionRequestApprovalParams { + thread_id, + turn_id, + item_id, + reason, + command, + cwd, + command_actions, + proposed_execpolicy_amendment, + } = params; + + println!( + "\n< commandExecution approval requested for thread {thread_id}, turn {turn_id}, item {item_id}" + ); + if let Some(reason) = reason.as_deref() { + println!("< reason: {reason}"); + } + if let Some(command) = command.as_deref() { + println!("< command: {command}"); + } + if let Some(cwd) = cwd.as_ref() { + println!("< cwd: {}", cwd.display()); + } + if let Some(command_actions) = command_actions.as_ref() + && !command_actions.is_empty() + { + println!("< command actions: {command_actions:?}"); + } + if let Some(execpolicy_amendment) = proposed_execpolicy_amendment.as_ref() { + println!("< proposed execpolicy amendment: {execpolicy_amendment:?}"); + } + + let response = CommandExecutionRequestApprovalResponse { + decision: CommandExecutionApprovalDecision::Accept, + }; + self.send_server_request_response(request_id, &response)?; + println!("< approved commandExecution request for item {item_id}"); + Ok(()) + } + + fn approve_file_change_request( + &mut self, + request_id: RequestId, + params: FileChangeRequestApprovalParams, + ) -> Result<()> { + let FileChangeRequestApprovalParams { + thread_id, + turn_id, + item_id, + reason, + grant_root, + } = params; + + println!( + "\n< fileChange approval requested for thread {thread_id}, turn {turn_id}, item {item_id}" + ); + if let Some(reason) = reason.as_deref() { + println!("< reason: {reason}"); + } + if let Some(grant_root) = grant_root.as_deref() { + println!("< grant root: {}", grant_root.display()); + } + + let response = FileChangeRequestApprovalResponse { + decision: FileChangeApprovalDecision::Accept, + }; + self.send_server_request_response(request_id, &response)?; + println!("< approved fileChange request for item {item_id}"); + Ok(()) + } + + fn send_server_request_response(&mut self, request_id: RequestId, response: &T) -> Result<()> + where + T: Serialize, + { + let message = JSONRPCMessage::Response(JSONRPCResponse { + id: request_id, + result: serde_json::to_value(response)?, + }); + self.write_jsonrpc_message(message) + } + + fn write_jsonrpc_message(&mut self, message: JSONRPCMessage) -> Result<()> { + let payload = serde_json::to_string(&message)?; + let pretty = serde_json::to_string_pretty(&message)?; + print_multiline_with_prefix("> ", &pretty); + + if let Some(stdin) = self.stdin.as_mut() { + writeln!(stdin, "{payload}")?; + stdin + .flush() + .context("failed to flush response to codex app-server")?; + return Ok(()); + } + + bail!("codex app-server stdin closed") + } +} + +fn print_multiline_with_prefix(prefix: &str, payload: &str) { + for line in payload.lines() { + println!("{prefix}{line}"); + } +} + +impl Drop for CodexClient { + fn drop(&mut self) { + let _ = self.stdin.take(); + + if let Ok(Some(status)) = self.child.try_wait() { + println!("[codex app-server exited: {status}]"); + return; + } + + thread::sleep(Duration::from_millis(100)); + + if let Ok(Some(status)) = self.child.try_wait() { + println!("[codex app-server exited: {status}]"); + return; + } + + let _ = self.child.kill(); + let _ = self.child.wait(); + } +} diff --git a/codex-rs/app-server-test-client/src/main.rs b/codex-rs/app-server-test-client/src/main.rs index 6d3fc06d81..a4da2e4026 100644 --- a/codex-rs/app-server-test-client/src/main.rs +++ b/codex-rs/app-server-test-client/src/main.rs @@ -1,1061 +1,5 @@ -use std::collections::VecDeque; -use std::fs; -use std::io::BufRead; -use std::io::BufReader; -use std::io::Write; -use std::path::Path; -use std::process::Child; -use std::process::ChildStdin; -use std::process::ChildStdout; -use std::process::Command; -use std::process::Stdio; -use std::thread; -use std::time::Duration; - -use anyhow::Context; use anyhow::Result; -use anyhow::bail; -use clap::ArgAction; -use clap::Parser; -use clap::Subcommand; -use codex_app_server_protocol::AddConversationListenerParams; -use codex_app_server_protocol::AddConversationSubscriptionResponse; -use codex_app_server_protocol::AskForApproval; -use codex_app_server_protocol::ClientInfo; -use codex_app_server_protocol::ClientRequest; -use codex_app_server_protocol::CommandExecutionApprovalDecision; -use codex_app_server_protocol::CommandExecutionRequestApprovalParams; -use codex_app_server_protocol::CommandExecutionRequestApprovalResponse; -use codex_app_server_protocol::DynamicToolSpec; -use codex_app_server_protocol::FileChangeApprovalDecision; -use codex_app_server_protocol::FileChangeRequestApprovalParams; -use codex_app_server_protocol::FileChangeRequestApprovalResponse; -use codex_app_server_protocol::GetAccountRateLimitsResponse; -use codex_app_server_protocol::InitializeCapabilities; -use codex_app_server_protocol::InitializeParams; -use codex_app_server_protocol::InitializeResponse; -use codex_app_server_protocol::InputItem; -use codex_app_server_protocol::JSONRPCMessage; -use codex_app_server_protocol::JSONRPCNotification; -use codex_app_server_protocol::JSONRPCRequest; -use codex_app_server_protocol::JSONRPCResponse; -use codex_app_server_protocol::LoginChatGptCompleteNotification; -use codex_app_server_protocol::LoginChatGptResponse; -use codex_app_server_protocol::ModelListParams; -use codex_app_server_protocol::ModelListResponse; -use codex_app_server_protocol::NewConversationParams; -use codex_app_server_protocol::NewConversationResponse; -use codex_app_server_protocol::RequestId; -use codex_app_server_protocol::SandboxPolicy; -use codex_app_server_protocol::SendUserMessageParams; -use codex_app_server_protocol::SendUserMessageResponse; -use codex_app_server_protocol::ServerNotification; -use codex_app_server_protocol::ServerRequest; -use codex_app_server_protocol::ThreadStartParams; -use codex_app_server_protocol::ThreadStartResponse; -use codex_app_server_protocol::TurnStartParams; -use codex_app_server_protocol::TurnStartResponse; -use codex_app_server_protocol::TurnStatus; -use codex_app_server_protocol::UserInput as V2UserInput; -use codex_protocol::ThreadId; -use codex_protocol::protocol::Event; -use codex_protocol::protocol::EventMsg; -use serde::Serialize; -use serde::de::DeserializeOwned; -use serde_json::Value; -use uuid::Uuid; - -/// Minimal launcher that initializes the Codex app-server and logs the handshake. -#[derive(Parser)] -#[command(author = "Codex", version, about = "Bootstrap Codex app-server", long_about = None)] -struct Cli { - /// Path to the `codex` CLI binary. - #[arg(long, env = "CODEX_BIN", default_value = "codex")] - codex_bin: String, - - /// Forwarded to the `codex` CLI as `--config key=value`. Repeatable. - /// - /// Example: - /// `--config 'model_providers.mock.base_url="http://localhost:4010/v2"'` - #[arg( - short = 'c', - long = "config", - value_name = "key=value", - action = ArgAction::Append, - global = true - )] - config_overrides: Vec, - - /// JSON array of dynamic tool specs or a single tool object. - /// Prefix a filename with '@' to read from a file. - /// - /// Example: - /// --dynamic-tools '[{"name":"demo","description":"Demo","inputSchema":{"type":"object"}}]' - /// --dynamic-tools @/path/to/tools.json - #[arg(long, value_name = "json-or-@file", global = true)] - dynamic_tools: Option, - - #[command(subcommand)] - command: CliCommand, -} - -#[derive(Subcommand)] -enum CliCommand { - /// Send a user message through the Codex app-server. - SendMessage { - /// User message to send to Codex. - #[arg()] - user_message: String, - }, - /// Send a user message through the app-server V2 thread/turn APIs. - SendMessageV2 { - /// User message to send to Codex. - #[arg()] - user_message: String, - }, - /// Start a V2 turn that elicits an ExecCommand approval. - #[command(name = "trigger-cmd-approval")] - TriggerCmdApproval { - /// Optional prompt; defaults to a simple python command. - #[arg()] - user_message: Option, - }, - /// Start a V2 turn that elicits an ApplyPatch approval. - #[command(name = "trigger-patch-approval")] - TriggerPatchApproval { - /// Optional prompt; defaults to creating a file via apply_patch. - #[arg()] - user_message: Option, - }, - /// Start a V2 turn that should not elicit an ExecCommand approval. - #[command(name = "no-trigger-cmd-approval")] - NoTriggerCmdApproval, - /// Send two sequential V2 turns in the same thread to test follow-up behavior. - SendFollowUpV2 { - /// Initial user message for the first turn. - #[arg()] - first_message: String, - /// Follow-up user message for the second turn. - #[arg()] - follow_up_message: String, - }, - /// Trigger the ChatGPT login flow and wait for completion. - TestLogin, - /// Fetch the current account rate limits from the Codex app-server. - GetAccountRateLimits, - /// List the available models from the Codex app-server. - #[command(name = "model-list")] - ModelList, -} fn main() -> Result<()> { - let Cli { - codex_bin, - config_overrides, - dynamic_tools, - command, - } = Cli::parse(); - - let dynamic_tools = parse_dynamic_tools_arg(&dynamic_tools)?; - - match command { - CliCommand::SendMessage { user_message } => { - ensure_dynamic_tools_unused(&dynamic_tools, "send-message")?; - send_message(&codex_bin, &config_overrides, user_message) - } - CliCommand::SendMessageV2 { user_message } => { - send_message_v2(&codex_bin, &config_overrides, user_message, &dynamic_tools) - } - CliCommand::TriggerCmdApproval { user_message } => { - trigger_cmd_approval(&codex_bin, &config_overrides, user_message, &dynamic_tools) - } - CliCommand::TriggerPatchApproval { user_message } => { - trigger_patch_approval(&codex_bin, &config_overrides, user_message, &dynamic_tools) - } - CliCommand::NoTriggerCmdApproval => { - no_trigger_cmd_approval(&codex_bin, &config_overrides, &dynamic_tools) - } - CliCommand::SendFollowUpV2 { - first_message, - follow_up_message, - } => send_follow_up_v2( - &codex_bin, - &config_overrides, - first_message, - follow_up_message, - &dynamic_tools, - ), - CliCommand::TestLogin => { - ensure_dynamic_tools_unused(&dynamic_tools, "test-login")?; - test_login(&codex_bin, &config_overrides) - } - CliCommand::GetAccountRateLimits => { - ensure_dynamic_tools_unused(&dynamic_tools, "get-account-rate-limits")?; - get_account_rate_limits(&codex_bin, &config_overrides) - } - CliCommand::ModelList => { - ensure_dynamic_tools_unused(&dynamic_tools, "model-list")?; - model_list(&codex_bin, &config_overrides) - } - } -} - -fn send_message(codex_bin: &str, config_overrides: &[String], user_message: String) -> Result<()> { - let mut client = CodexClient::spawn(codex_bin, config_overrides)?; - - let initialize = client.initialize()?; - println!("< initialize response: {initialize:?}"); - - let conversation = client.start_thread()?; - println!("< newConversation response: {conversation:?}"); - - let subscription = client.add_conversation_listener(&conversation.conversation_id)?; - println!("< addConversationListener response: {subscription:?}"); - - let send_response = client.send_user_message(&conversation.conversation_id, &user_message)?; - println!("< sendUserMessage response: {send_response:?}"); - - client.stream_conversation(&conversation.conversation_id)?; - - client.remove_thread_listener(subscription.subscription_id)?; - - Ok(()) -} - -fn send_message_v2( - codex_bin: &str, - config_overrides: &[String], - user_message: String, - dynamic_tools: &Option>, -) -> Result<()> { - send_message_v2_with_policies( - codex_bin, - config_overrides, - user_message, - None, - None, - dynamic_tools, - ) -} - -fn trigger_cmd_approval( - codex_bin: &str, - config_overrides: &[String], - user_message: Option, - dynamic_tools: &Option>, -) -> Result<()> { - let default_prompt = - "Run `touch /tmp/should-trigger-approval` so I can confirm the file exists."; - let message = user_message.unwrap_or_else(|| default_prompt.to_string()); - send_message_v2_with_policies( - codex_bin, - config_overrides, - message, - Some(AskForApproval::OnRequest), - Some(SandboxPolicy::ReadOnly), - dynamic_tools, - ) -} - -fn trigger_patch_approval( - codex_bin: &str, - config_overrides: &[String], - user_message: Option, - dynamic_tools: &Option>, -) -> Result<()> { - let default_prompt = - "Create a file named APPROVAL_DEMO.txt containing a short hello message using apply_patch."; - let message = user_message.unwrap_or_else(|| default_prompt.to_string()); - send_message_v2_with_policies( - codex_bin, - config_overrides, - message, - Some(AskForApproval::OnRequest), - Some(SandboxPolicy::ReadOnly), - dynamic_tools, - ) -} - -fn no_trigger_cmd_approval( - codex_bin: &str, - config_overrides: &[String], - dynamic_tools: &Option>, -) -> Result<()> { - let prompt = "Run `touch should_not_trigger_approval.txt`"; - send_message_v2_with_policies( - codex_bin, - config_overrides, - prompt.to_string(), - None, - None, - dynamic_tools, - ) -} - -fn send_message_v2_with_policies( - codex_bin: &str, - config_overrides: &[String], - user_message: String, - approval_policy: Option, - sandbox_policy: Option, - dynamic_tools: &Option>, -) -> Result<()> { - let mut client = CodexClient::spawn(codex_bin, config_overrides)?; - - let initialize = client.initialize()?; - println!("< initialize response: {initialize:?}"); - - let thread_response = client.thread_start(ThreadStartParams { - dynamic_tools: dynamic_tools.clone(), - ..Default::default() - })?; - println!("< thread/start response: {thread_response:?}"); - let mut turn_params = TurnStartParams { - thread_id: thread_response.thread.id.clone(), - input: vec![V2UserInput::Text { - text: user_message, - // Test client sends plain text without UI element ranges. - text_elements: Vec::new(), - }], - ..Default::default() - }; - turn_params.approval_policy = approval_policy; - turn_params.sandbox_policy = sandbox_policy; - - let turn_response = client.turn_start(turn_params)?; - println!("< turn/start response: {turn_response:?}"); - - client.stream_turn(&thread_response.thread.id, &turn_response.turn.id)?; - - Ok(()) -} - -fn send_follow_up_v2( - codex_bin: &str, - config_overrides: &[String], - first_message: String, - follow_up_message: String, - dynamic_tools: &Option>, -) -> Result<()> { - let mut client = CodexClient::spawn(codex_bin, config_overrides)?; - - let initialize = client.initialize()?; - println!("< initialize response: {initialize:?}"); - - let thread_response = client.thread_start(ThreadStartParams { - dynamic_tools: dynamic_tools.clone(), - ..Default::default() - })?; - println!("< thread/start response: {thread_response:?}"); - - let first_turn_params = TurnStartParams { - thread_id: thread_response.thread.id.clone(), - input: vec![V2UserInput::Text { - text: first_message, - // Test client sends plain text without UI element ranges. - text_elements: Vec::new(), - }], - ..Default::default() - }; - let first_turn_response = client.turn_start(first_turn_params)?; - println!("< turn/start response (initial): {first_turn_response:?}"); - client.stream_turn(&thread_response.thread.id, &first_turn_response.turn.id)?; - - let follow_up_params = TurnStartParams { - thread_id: thread_response.thread.id.clone(), - input: vec![V2UserInput::Text { - text: follow_up_message, - // Test client sends plain text without UI element ranges. - text_elements: Vec::new(), - }], - ..Default::default() - }; - let follow_up_response = client.turn_start(follow_up_params)?; - println!("< turn/start response (follow-up): {follow_up_response:?}"); - client.stream_turn(&thread_response.thread.id, &follow_up_response.turn.id)?; - - Ok(()) -} - -fn test_login(codex_bin: &str, config_overrides: &[String]) -> Result<()> { - let mut client = CodexClient::spawn(codex_bin, config_overrides)?; - - let initialize = client.initialize()?; - println!("< initialize response: {initialize:?}"); - - let login_response = client.login_chat_gpt()?; - println!("< loginChatGpt response: {login_response:?}"); - println!( - "Open the following URL in your browser to continue:\n{}", - login_response.auth_url - ); - - let completion = client.wait_for_login_completion(&login_response.login_id)?; - println!("< loginChatGptComplete notification: {completion:?}"); - - if completion.success { - println!("Login succeeded."); - Ok(()) - } else { - bail!( - "login failed: {}", - completion - .error - .as_deref() - .unwrap_or("unknown error from loginChatGptComplete") - ); - } -} - -fn get_account_rate_limits(codex_bin: &str, config_overrides: &[String]) -> Result<()> { - let mut client = CodexClient::spawn(codex_bin, config_overrides)?; - - let initialize = client.initialize()?; - println!("< initialize response: {initialize:?}"); - - let response = client.get_account_rate_limits()?; - println!("< account/rateLimits/read response: {response:?}"); - - Ok(()) -} - -fn model_list(codex_bin: &str, config_overrides: &[String]) -> Result<()> { - let mut client = CodexClient::spawn(codex_bin, config_overrides)?; - - let initialize = client.initialize()?; - println!("< initialize response: {initialize:?}"); - - let response = client.model_list(ModelListParams::default())?; - println!("< model/list response: {response:?}"); - - Ok(()) -} - -fn ensure_dynamic_tools_unused( - dynamic_tools: &Option>, - command: &str, -) -> Result<()> { - if dynamic_tools.is_some() { - bail!( - "dynamic tools are only supported for v2 thread/start; remove --dynamic-tools for {command} or use send-message-v2" - ); - } - Ok(()) -} - -fn parse_dynamic_tools_arg(dynamic_tools: &Option) -> Result>> { - let Some(raw_arg) = dynamic_tools.as_deref() else { - return Ok(None); - }; - - let raw_json = if let Some(path) = raw_arg.strip_prefix('@') { - fs::read_to_string(Path::new(path)) - .with_context(|| format!("read dynamic tools file {path}"))? - } else { - raw_arg.to_string() - }; - - let value: Value = serde_json::from_str(&raw_json).context("parse dynamic tools JSON")?; - let tools = match value { - Value::Array(_) => serde_json::from_value(value).context("decode dynamic tools array")?, - Value::Object(_) => vec![serde_json::from_value(value).context("decode dynamic tool")?], - _ => bail!("dynamic tools JSON must be an object or array"), - }; - - Ok(Some(tools)) -} - -struct CodexClient { - child: Child, - stdin: Option, - stdout: BufReader, - pending_notifications: VecDeque, -} - -impl CodexClient { - fn spawn(codex_bin: &str, config_overrides: &[String]) -> Result { - let mut cmd = Command::new(codex_bin); - for override_kv in config_overrides { - cmd.arg("--config").arg(override_kv); - } - let mut codex_app_server = cmd - .arg("app-server") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::inherit()) - .spawn() - .with_context(|| format!("failed to start `{codex_bin}` app-server"))?; - - let stdin = codex_app_server - .stdin - .take() - .context("codex app-server stdin unavailable")?; - let stdout = codex_app_server - .stdout - .take() - .context("codex app-server stdout unavailable")?; - - Ok(Self { - child: codex_app_server, - stdin: Some(stdin), - stdout: BufReader::new(stdout), - pending_notifications: VecDeque::new(), - }) - } - - fn initialize(&mut self) -> Result { - let request_id = self.request_id(); - let request = ClientRequest::Initialize { - request_id: request_id.clone(), - params: InitializeParams { - client_info: ClientInfo { - name: "codex-toy-app-server".to_string(), - title: Some("Codex Toy App Server".to_string()), - version: env!("CARGO_PKG_VERSION").to_string(), - }, - capabilities: Some(InitializeCapabilities { - experimental_api: true, - }), - }, - }; - - self.send_request(request, request_id, "initialize") - } - - fn start_thread(&mut self) -> Result { - let request_id = self.request_id(); - let request = ClientRequest::NewConversation { - request_id: request_id.clone(), - params: NewConversationParams::default(), - }; - - self.send_request(request, request_id, "newConversation") - } - - fn add_conversation_listener( - &mut self, - conversation_id: &ThreadId, - ) -> Result { - let request_id = self.request_id(); - let request = ClientRequest::AddConversationListener { - request_id: request_id.clone(), - params: AddConversationListenerParams { - conversation_id: *conversation_id, - experimental_raw_events: false, - }, - }; - - self.send_request(request, request_id, "addConversationListener") - } - - fn remove_thread_listener(&mut self, subscription_id: Uuid) -> Result<()> { - let request_id = self.request_id(); - let request = ClientRequest::RemoveConversationListener { - request_id: request_id.clone(), - params: codex_app_server_protocol::RemoveConversationListenerParams { subscription_id }, - }; - - self.send_request::( - request, - request_id, - "removeConversationListener", - )?; - - Ok(()) - } - - fn send_user_message( - &mut self, - conversation_id: &ThreadId, - message: &str, - ) -> Result { - let request_id = self.request_id(); - let request = ClientRequest::SendUserMessage { - request_id: request_id.clone(), - params: SendUserMessageParams { - conversation_id: *conversation_id, - items: vec![InputItem::Text { - text: message.to_string(), - // Test client sends plain text without UI element ranges. - text_elements: Vec::new(), - }], - }, - }; - - self.send_request(request, request_id, "sendUserMessage") - } - - fn thread_start(&mut self, params: ThreadStartParams) -> Result { - let request_id = self.request_id(); - let request = ClientRequest::ThreadStart { - request_id: request_id.clone(), - params, - }; - - self.send_request(request, request_id, "thread/start") - } - - fn turn_start(&mut self, params: TurnStartParams) -> Result { - let request_id = self.request_id(); - let request = ClientRequest::TurnStart { - request_id: request_id.clone(), - params, - }; - - self.send_request(request, request_id, "turn/start") - } - - fn login_chat_gpt(&mut self) -> Result { - let request_id = self.request_id(); - let request = ClientRequest::LoginChatGpt { - request_id: request_id.clone(), - params: None, - }; - - self.send_request(request, request_id, "loginChatGpt") - } - - fn get_account_rate_limits(&mut self) -> Result { - let request_id = self.request_id(); - let request = ClientRequest::GetAccountRateLimits { - request_id: request_id.clone(), - params: None, - }; - - self.send_request(request, request_id, "account/rateLimits/read") - } - - fn model_list(&mut self, params: ModelListParams) -> Result { - let request_id = self.request_id(); - let request = ClientRequest::ModelList { - request_id: request_id.clone(), - params, - }; - - self.send_request(request, request_id, "model/list") - } - - fn stream_conversation(&mut self, conversation_id: &ThreadId) -> Result<()> { - loop { - let notification = self.next_notification()?; - - if !notification.method.starts_with("codex/event/") { - continue; - } - - if let Some(event) = self.extract_event(notification, conversation_id)? { - match &event.msg { - EventMsg::AgentMessage(event) => { - println!("{}", event.message); - } - EventMsg::AgentMessageDelta(event) => { - print!("{}", event.delta); - std::io::stdout().flush().ok(); - } - EventMsg::TurnComplete(event) => { - println!("\n[task complete: {event:?}]"); - break; - } - EventMsg::TurnAborted(event) => { - println!("\n[turn aborted: {:?}]", event.reason); - break; - } - EventMsg::Error(event) => { - println!("[error] {event:?}"); - } - _ => { - println!("[UNKNOWN EVENT] {:?}", event.msg); - } - } - } - } - - Ok(()) - } - - fn wait_for_login_completion( - &mut self, - expected_login_id: &Uuid, - ) -> Result { - loop { - let notification = self.next_notification()?; - - if let Ok(server_notification) = ServerNotification::try_from(notification) { - match server_notification { - ServerNotification::LoginChatGptComplete(completion) => { - if &completion.login_id == expected_login_id { - return Ok(completion); - } - - println!( - "[ignoring loginChatGptComplete for unexpected login_id: {}]", - completion.login_id - ); - } - ServerNotification::AuthStatusChange(status) => { - println!("< authStatusChange notification: {status:?}"); - } - ServerNotification::AccountRateLimitsUpdated(snapshot) => { - println!("< accountRateLimitsUpdated notification: {snapshot:?}"); - } - ServerNotification::SessionConfigured(_) => { - // SessionConfigured notifications are unrelated to login; skip. - } - _ => {} - } - } - - // Not a server notification (likely a conversation event); keep waiting. - } - } - - fn stream_turn(&mut self, thread_id: &str, turn_id: &str) -> Result<()> { - loop { - let notification = self.next_notification()?; - - let Ok(server_notification) = ServerNotification::try_from(notification) else { - continue; - }; - - match server_notification { - ServerNotification::ThreadStarted(payload) => { - if payload.thread.id == thread_id { - println!("< thread/started notification: {:?}", payload.thread); - } - } - ServerNotification::TurnStarted(payload) => { - if payload.turn.id == turn_id { - println!("< turn/started notification: {:?}", payload.turn.status); - } - } - ServerNotification::AgentMessageDelta(delta) => { - print!("{}", delta.delta); - std::io::stdout().flush().ok(); - } - ServerNotification::CommandExecutionOutputDelta(delta) => { - print!("{}", delta.delta); - std::io::stdout().flush().ok(); - } - ServerNotification::TerminalInteraction(delta) => { - println!("[stdin sent: {}]", delta.stdin); - std::io::stdout().flush().ok(); - } - ServerNotification::ItemStarted(payload) => { - println!("\n< item started: {:?}", payload.item); - } - ServerNotification::ItemCompleted(payload) => { - println!("< item completed: {:?}", payload.item); - } - ServerNotification::TurnCompleted(payload) => { - if payload.turn.id == turn_id { - println!("\n< turn/completed notification: {:?}", payload.turn.status); - if payload.turn.status == TurnStatus::Failed - && let Some(error) = payload.turn.error - { - println!("[turn error] {}", error.message); - } - break; - } - } - ServerNotification::McpToolCallProgress(payload) => { - println!("< MCP tool progress: {}", payload.message); - } - _ => { - println!("[UNKNOWN SERVER NOTIFICATION] {server_notification:?}"); - } - } - } - - Ok(()) - } - - fn extract_event( - &self, - notification: JSONRPCNotification, - conversation_id: &ThreadId, - ) -> Result> { - let params = notification - .params - .context("event notification missing params")?; - - let mut map = match params { - Value::Object(map) => map, - other => bail!("unexpected params shape: {other:?}"), - }; - - let conversation_value = map - .remove("conversationId") - .context("event missing conversationId")?; - let notification_conversation: ThreadId = serde_json::from_value(conversation_value) - .context("conversationId was not a valid UUID")?; - - if ¬ification_conversation != conversation_id { - return Ok(None); - } - - let event_value = Value::Object(map); - let event: Event = - serde_json::from_value(event_value).context("failed to decode event payload")?; - Ok(Some(event)) - } - - fn send_request( - &mut self, - request: ClientRequest, - request_id: RequestId, - method: &str, - ) -> Result - where - T: DeserializeOwned, - { - self.write_request(&request)?; - self.wait_for_response(request_id, method) - } - - fn write_request(&mut self, request: &ClientRequest) -> Result<()> { - let request_json = serde_json::to_string(request)?; - let request_pretty = serde_json::to_string_pretty(request)?; - print_multiline_with_prefix("> ", &request_pretty); - - if let Some(stdin) = self.stdin.as_mut() { - writeln!(stdin, "{request_json}")?; - stdin - .flush() - .context("failed to flush request to codex app-server")?; - } else { - bail!("codex app-server stdin closed"); - } - - Ok(()) - } - - fn wait_for_response(&mut self, request_id: RequestId, method: &str) -> Result - where - T: DeserializeOwned, - { - loop { - let message = self.read_jsonrpc_message()?; - - match message { - JSONRPCMessage::Response(JSONRPCResponse { id, result }) => { - if id == request_id { - return serde_json::from_value(result) - .with_context(|| format!("{method} response missing payload")); - } - } - JSONRPCMessage::Error(err) => { - if err.id == request_id { - bail!("{method} failed: {err:?}"); - } - } - JSONRPCMessage::Notification(notification) => { - self.pending_notifications.push_back(notification); - } - JSONRPCMessage::Request(request) => { - self.handle_server_request(request)?; - } - } - } - } - - fn next_notification(&mut self) -> Result { - if let Some(notification) = self.pending_notifications.pop_front() { - return Ok(notification); - } - - loop { - let message = self.read_jsonrpc_message()?; - - match message { - JSONRPCMessage::Notification(notification) => return Ok(notification), - JSONRPCMessage::Response(_) | JSONRPCMessage::Error(_) => { - // No outstanding requests, so ignore stray responses/errors for now. - continue; - } - JSONRPCMessage::Request(request) => { - self.handle_server_request(request)?; - } - } - } - } - - fn read_jsonrpc_message(&mut self) -> Result { - loop { - let mut response_line = String::new(); - let bytes = self - .stdout - .read_line(&mut response_line) - .context("failed to read from codex app-server")?; - - if bytes == 0 { - bail!("codex app-server closed stdout"); - } - - let trimmed = response_line.trim(); - if trimmed.is_empty() { - continue; - } - - let parsed: Value = - serde_json::from_str(trimmed).context("response was not valid JSON-RPC")?; - let pretty = serde_json::to_string_pretty(&parsed)?; - print_multiline_with_prefix("< ", &pretty); - let message: JSONRPCMessage = serde_json::from_value(parsed) - .context("response was not a valid JSON-RPC message")?; - return Ok(message); - } - } - - fn request_id(&self) -> RequestId { - RequestId::String(Uuid::new_v4().to_string()) - } - - fn handle_server_request(&mut self, request: JSONRPCRequest) -> Result<()> { - let server_request = ServerRequest::try_from(request) - .context("failed to deserialize ServerRequest from JSONRPCRequest")?; - - match server_request { - ServerRequest::CommandExecutionRequestApproval { request_id, params } => { - self.handle_command_execution_request_approval(request_id, params)?; - } - ServerRequest::FileChangeRequestApproval { request_id, params } => { - self.approve_file_change_request(request_id, params)?; - } - other => { - bail!("received unsupported server request: {other:?}"); - } - } - - Ok(()) - } - - fn handle_command_execution_request_approval( - &mut self, - request_id: RequestId, - params: CommandExecutionRequestApprovalParams, - ) -> Result<()> { - let CommandExecutionRequestApprovalParams { - thread_id, - turn_id, - item_id, - reason, - command, - cwd, - command_actions, - proposed_execpolicy_amendment, - } = params; - - println!( - "\n< commandExecution approval requested for thread {thread_id}, turn {turn_id}, item {item_id}" - ); - if let Some(reason) = reason.as_deref() { - println!("< reason: {reason}"); - } - if let Some(command) = command.as_deref() { - println!("< command: {command}"); - } - if let Some(cwd) = cwd.as_ref() { - println!("< cwd: {}", cwd.display()); - } - if let Some(command_actions) = command_actions.as_ref() - && !command_actions.is_empty() - { - println!("< command actions: {command_actions:?}"); - } - if let Some(execpolicy_amendment) = proposed_execpolicy_amendment.as_ref() { - println!("< proposed execpolicy amendment: {execpolicy_amendment:?}"); - } - - let response = CommandExecutionRequestApprovalResponse { - decision: CommandExecutionApprovalDecision::Accept, - }; - self.send_server_request_response(request_id, &response)?; - println!("< approved commandExecution request for item {item_id}"); - Ok(()) - } - - fn approve_file_change_request( - &mut self, - request_id: RequestId, - params: FileChangeRequestApprovalParams, - ) -> Result<()> { - let FileChangeRequestApprovalParams { - thread_id, - turn_id, - item_id, - reason, - grant_root, - } = params; - - println!( - "\n< fileChange approval requested for thread {thread_id}, turn {turn_id}, item {item_id}" - ); - if let Some(reason) = reason.as_deref() { - println!("< reason: {reason}"); - } - if let Some(grant_root) = grant_root.as_deref() { - println!("< grant root: {}", grant_root.display()); - } - - let response = FileChangeRequestApprovalResponse { - decision: FileChangeApprovalDecision::Accept, - }; - self.send_server_request_response(request_id, &response)?; - println!("< approved fileChange request for item {item_id}"); - Ok(()) - } - - fn send_server_request_response(&mut self, request_id: RequestId, response: &T) -> Result<()> - where - T: Serialize, - { - let message = JSONRPCMessage::Response(JSONRPCResponse { - id: request_id, - result: serde_json::to_value(response)?, - }); - self.write_jsonrpc_message(message) - } - - fn write_jsonrpc_message(&mut self, message: JSONRPCMessage) -> Result<()> { - let payload = serde_json::to_string(&message)?; - let pretty = serde_json::to_string_pretty(&message)?; - print_multiline_with_prefix("> ", &pretty); - - if let Some(stdin) = self.stdin.as_mut() { - writeln!(stdin, "{payload}")?; - stdin - .flush() - .context("failed to flush response to codex app-server")?; - return Ok(()); - } - - bail!("codex app-server stdin closed") - } -} - -fn print_multiline_with_prefix(prefix: &str, payload: &str) { - for line in payload.lines() { - println!("{prefix}{line}"); - } -} - -impl Drop for CodexClient { - fn drop(&mut self) { - let _ = self.stdin.take(); - - if let Ok(Some(status)) = self.child.try_wait() { - println!("[codex app-server exited: {status}]"); - return; - } - - thread::sleep(Duration::from_millis(100)); - - if let Ok(Some(status)) = self.child.try_wait() { - println!("[codex app-server exited: {status}]"); - return; - } - - let _ = self.child.kill(); - let _ = self.child.wait(); - } + codex_app_server_test_client::run() } diff --git a/codex-rs/app-server/Cargo.toml b/codex-rs/app-server/Cargo.toml index 778d57bab0..2f6011a634 100644 --- a/codex-rs/app-server/Cargo.toml +++ b/codex-rs/app-server/Cargo.toml @@ -33,6 +33,8 @@ codex-rmcp-client = { workspace = true } codex-utils-absolute-path = { workspace = true } codex-utils-json-to-toml = { workspace = true } chrono = { workspace = true } +clap = { workspace = true, features = ["derive"] } +futures = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tempfile = { workspace = true } @@ -45,6 +47,7 @@ tokio = { workspace = true, features = [ "rt-multi-thread", "signal", ] } +tokio-tungstenite = { workspace = true } tracing = { workspace = true, features = ["log"] } tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } uuid = { workspace = true, features = ["serde", "v7"] } @@ -59,6 +62,7 @@ axum = { workspace = true, default-features = false, features = [ base64 = { workspace = true } codex-execpolicy = { workspace = true } core_test_support = { workspace = true } +codex-utils-cargo-bin = { workspace = true } os_info = { workspace = true } pretty_assertions = { workspace = true } rmcp = { workspace = true, default-features = false, features = [ @@ -66,5 +70,6 @@ rmcp = { workspace = true, default-features = false, features = [ "transport-streamable-http-server", ] } serial_test = { workspace = true } +tokio-tungstenite = { workspace = true } wiremock = { workspace = true } shlex = { workspace = true } diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index d6fae48466..a6d49f2e4c 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -15,11 +15,18 @@ - [Skills](#skills) - [Apps](#apps) - [Auth endpoints](#auth-endpoints) -- [Adding an experimental field](#adding-an-experimental-field) +- [Experimental API Opt-in](#experimental-api-opt-in) ## Protocol -Similar to [MCP](https://modelcontextprotocol.io/), `codex app-server` supports bidirectional communication, streaming JSONL over stdio. The protocol is JSON-RPC 2.0, though the `"jsonrpc":"2.0"` header is omitted. +Similar to [MCP](https://modelcontextprotocol.io/), `codex app-server` supports bidirectional communication using JSON-RPC 2.0 messages (with the `"jsonrpc":"2.0"` header omitted on the wire). + +Supported transports: + +- stdio (`--listen stdio://`, default): newline-delimited JSON (JSONL) +- websocket (`--listen ws://IP:PORT`): one JSON-RPC message per websocket text frame (**experimental / unsupported**) + +Websocket transport is currently experimental and unsupported. Do not rely on it for production workloads. ## Message Schema @@ -42,7 +49,7 @@ Use the thread APIs to create, list, or archive conversations. Drive a conversat ## Lifecycle Overview -- Initialize once: Immediately after launching the codex app-server process, send an `initialize` request with your client metadata, then emit an `initialized` notification. Any other request before this handshake gets rejected. +- Initialize once per connection: Immediately after opening a transport connection, send an `initialize` request with your client metadata, then emit an `initialized` notification. Any other request on that connection before this handshake gets rejected. - Start (or resume) a thread: Call `thread/start` to open a fresh conversation. The response returns the thread object and you’ll also get a `thread/started` notification. If you’re continuing an existing conversation, call `thread/resume` with its ID instead. If you want to branch from an existing conversation, call `thread/fork` to create a new thread id with copied history. - Begin a turn: To send user input, call `turn/start` with the target `threadId` and the user's input. Optional fields let you override model, cwd, sandbox policy, etc. This immediately returns the new turn object and triggers a `turn/started` notification. - Stream events: After `turn/start`, keep reading JSON-RPC notifications on stdout. You’ll see `item/started`, `item/completed`, deltas like `item/agentMessage/delta`, tool progress, etc. These represent streaming model output plus any side effects (commands, tool calls, reasoning notes). @@ -50,7 +57,7 @@ Use the thread APIs to create, list, or archive conversations. Drive a conversat ## Initialization -Clients must send a single `initialize` request before invoking any other method, then acknowledge with an `initialized` notification. The server returns the user agent string it will present to upstream services; subsequent requests issued before initialization receive a `"Not initialized"` error, and repeated `initialize` calls receive an `"Already initialized"` error. +Clients must send a single `initialize` request per transport connection before invoking any other method on that connection, then acknowledge with an `initialized` notification. The server returns the user agent string it will present to upstream services; subsequent requests issued before initialization receive a `"Not initialized"` error, and repeated `initialize` calls on the same connection receive an `"Already initialized"` error. Applications building on top of `codex app-server` should identify themselves via the `clientInfo` parameter. @@ -85,12 +92,15 @@ Example (from OpenAI's official VSCode extension): - `thread/archive` — move a thread’s rollout file into the archived directory; returns `{}` on success. - `thread/name/set` — set or update a thread’s user-facing name; returns `{}` on success. Thread names are not required to be unique; name lookups resolve to the most recently updated thread. - `thread/unarchive` — move an archived rollout file back into the sessions directory; returns the restored `thread` on success. +- `thread/compact/start` — trigger conversation history compaction for a thread; returns `{}` immediately while progress streams through standard turn/item notifications. - `thread/rollback` — drop the last N turns from the agent’s in-memory context and persist a rollback marker in the rollout so future resumes see the pruned history; returns the updated `thread` (with `turns` populated) on success. - `turn/start` — add user input to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications. +- `turn/steer` — add user input to an already in-flight turn without starting a new turn; returns the active `turnId` that accepted the input. - `turn/interrupt` — request cancellation of an in-flight turn by `(thread_id, turn_id)`; success is an empty `{}` response and the turn finishes with `status: "interrupted"`. - `review/start` — kick off Codex’s automated reviewer for a thread; responds like `turn/start` and emits `item/started`/`item/completed` notifications with `enteredReviewMode` and `exitedReviewMode` items, plus a final assistant `agentMessage` containing the review. - `command/exec` — run a single command under the server sandbox without starting a thread/turn (handy for utilities and validation). -- `model/list` — list available models (with reasoning effort options). +- `model/list` — list available models (with reasoning effort options and optional `upgrade` model ids). +- `experimentalFeature/list` — list feature flags with stage metadata (`beta`, `underDevelopment`, `stable`, etc.), enabled/default-enabled state, and cursor pagination. For non-beta flags, `displayName`/`description`/`announcement` are `null`. - `collaborationMode/list` — list available collaboration mode presets (experimental, no pagination). - `skills/list` — list skills for one or more `cwd` values (optional `forceReload`). - `skills/remote/read` — list public remote skills (**under development; do not call from production clients yet**). @@ -121,6 +131,7 @@ Start a fresh thread when you need a new Codex conversation. "approvalPolicy": "never", "sandbox": "workspaceWrite", "personality": "friendly", + // Experimental: requires opt-in "dynamicTools": [ { "name": "lookup_ticket", @@ -146,6 +157,8 @@ Start a fresh thread when you need a new Codex conversation. { "method": "thread/started", "params": { "thread": { … } } } ``` +Valid `personality` values are `"friendly"`, `"pragmatic"`, and `"none"`. When `"none"` is selected, the personality placeholder is replaced with an empty string. + To continue a stored session, call `thread/resume` with the `thread.id` you previously recorded. The response shape matches `thread/start`, and no additional notifications are emitted. You can also pass the same configuration overrides supported by `thread/start`, such as `personality`: ```json @@ -239,6 +252,22 @@ Use `thread/unarchive` to move an archived rollout back into the sessions direct { "id": 24, "result": { "thread": { "id": "thr_b" } } } ``` +### Example: Trigger thread compaction + +Use `thread/compact/start` to trigger manual history compaction for a thread. The request returns immediately with `{}`. + +Progress is emitted as standard `turn/*` and `item/*` notifications on the same `threadId`. Clients should expect a single compaction item: + +- `item/started` with `item: { "type": "contextCompaction", ... }` +- `item/completed` with the same `contextCompaction` item id + +While compaction is running, the thread is effectively in a turn so clients should surface progress UI based on the notifications. + +```json +{ "method": "thread/compact/start", "id": 25, "params": { "threadId": "thr_b" } } +{ "id": 25, "result": {} } +``` + ### Example: Start a turn (send user input) Turns attach user input (text or images) to a thread and trigger Codex generation. The `input` field is a list of discriminated unions: @@ -335,6 +364,22 @@ You can cancel a running Turn with `turn/interrupt`. The server requests cancellations for running subprocesses, then emits a `turn/completed` event with `status: "interrupted"`. Rely on the `turn/completed` to know when Codex-side cleanup is done. +### Example: Steer an active turn + +Use `turn/steer` to append additional user input to the currently active turn. This does not emit +`turn/started` and does not accept turn context overrides. + +```json +{ "method": "turn/steer", "id": 32, "params": { + "threadId": "thr_123", + "input": [ { "type": "text", "text": "Actually focus on failing tests first." } ], + "expectedTurnId": "turn_456" +} } +{ "id": 32, "result": { "turnId": "turn_456" } } +``` + +`expectedTurnId` is required. If there is no active turn (or `expectedTurnId` does not match the active turn), the request fails with an `invalid request` error. + ### Example: Request a code review Use `review/start` to run Codex’s reviewer on the currently checked-out project. The request takes the thread id plus a `target` describing what should be reviewed: @@ -537,6 +582,41 @@ Order of messages: UI guidance for IDEs: surface an approval dialog as soon as the request arrives. The turn will proceed after the server receives a response to the approval request. The terminal `item/completed` notification will be sent with the appropriate status. +### Dynamic tool calls (experimental) + +`dynamicTools` on `thread/start` and the corresponding `item/tool/call` request/response flow are experimental APIs. To enable them, set `initialize.params.capabilities.experimentalApi = true`. + +When a dynamic tool is invoked during a turn, the server sends an `item/tool/call` JSON-RPC request to the client: + +```json +{ + "method": "item/tool/call", + "id": 60, + "params": { + "threadId": "thr_123", + "turnId": "turn_123", + "callId": "call_123", + "tool": "lookup_ticket", + "arguments": { "id": "ABC-123" } + } +} +``` + +The client must respond with content items. Use `inputText` for text and `inputImage` for image URLs/data URLs: + +```json +{ + "id": 60, + "result": { + "contentItems": [ + { "type": "inputText", "text": "Ticket ABC-123 is open." }, + { "type": "inputImage", "imageUrl": "data:image/png;base64,AAA" } + ], + "success": true + } +} +``` + ## Skills Invoke a skill by including `$` in the text input. Add a `skill` input item (recommended) so the backend injects full skill instructions instead of relying on the model to resolve the name. @@ -772,7 +852,68 @@ Field notes: - `windowDurationMins` is the quota window length. - `resetsAt` is a Unix timestamp (seconds) for the next reset. -## Adding an experimental field +## Experimental API Opt-in + +Some app-server methods and fields are intentionally gated behind an experimental capability with no backwards-compatible guarantees. This lets clients choose between: + +- Stable surface only (default): no opt-in, no experimental methods/fields exposed. +- Experimental surface: opt in during `initialize`. + +### Generating stable vs experimental client schemas + +`codex app-server` schema generation defaults to the stable API surface (experimental fields and methods filtered out). Pass `--experimental` to include experimental methods/fields in generated TypeScript or JSON schema: + +```bash +# Stable-only output (default) +codex app-server generate-ts --out DIR +codex app-server generate-json-schema --out DIR + +# Include experimental API surface +codex app-server generate-ts --out DIR --experimental +codex app-server generate-json-schema --out DIR --experimental +``` + +### How clients opt in at runtime + +Set `capabilities.experimentalApi` to `true` in your single `initialize` request: + +```json +{ + "method": "initialize", + "id": 1, + "params": { + "clientInfo": { + "name": "my_client", + "title": "My Client", + "version": "0.1.0" + }, + "capabilities": { + "experimentalApi": true + } + } +} +``` + +Then send the standard `initialized` notification and proceed normally. + +Notes: + +- If `capabilities` is omitted, `experimentalApi` is treated as `false`. +- This setting is negotiated once at initialization time for the process lifetime (re-initializing is rejected with `"Already initialized"`). + +### What happens without opt-in + +If a request uses an experimental method or sets an experimental field without opting in, app-server rejects it with a JSON-RPC error. The message is: + +` requires experimentalApi capability` + +Examples of descriptor strings: + +- `mock/experimentalMethod` (method-level gate) +- `thread/start.mockExperimentalField` (field-level gate) + +### For maintainers: Adding experimental fields and methods + Use this checklist when introducing a field/method that should only be available when the client opts into experimental APIs. At runtime, clients must send `initialize` with `capabilities.experimentalApi = true` to use experimental methods or fields. @@ -793,7 +934,7 @@ At runtime, clients must send `initialize` with `capabilities.experimentalApi = # Include experimental API fields/methods in fixtures. just write-app-server-schema --experimental ``` - + 5. Verify the protocol crate: ```bash diff --git a/codex-rs/app-server/src/bespoke_event_handling.rs b/codex-rs/app-server/src/bespoke_event_handling.rs index 55b185e5a8..1d3d7ff059 100644 --- a/codex-rs/app-server/src/bespoke_event_handling.rs +++ b/codex-rs/app-server/src/bespoke_event_handling.rs @@ -88,6 +88,7 @@ use codex_core::protocol::TurnDiffEvent; use codex_core::review_format::format_review_findings_block; use codex_core::review_prompts; use codex_protocol::ThreadId; +use codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem as CoreDynamicToolCallOutputContentItem; use codex_protocol::dynamic_tools::DynamicToolResponse as CoreDynamicToolResponse; use codex_protocol::plan_tool::UpdatePlanArgs; use codex_protocol::protocol::ReviewOutputEvent; @@ -351,8 +352,9 @@ pub(crate) async fn apply_bespoke_event_handling( .submit(Op::DynamicToolResponse { id: call_id.clone(), response: CoreDynamicToolResponse { - call_id, - output: "dynamic tool calls require api v2".to_string(), + content_items: vec![CoreDynamicToolCallOutputContentItem::InputText { + text: "dynamic tool calls require api v2".to_string(), + }], success: false, }, }) @@ -1091,7 +1093,7 @@ pub(crate) async fn apply_bespoke_event_handling( ), data: None, }; - outgoing.send_error(request_id, error).await; + outgoing.send_error(request_id.clone(), error).await; return; } } @@ -1105,7 +1107,7 @@ pub(crate) async fn apply_bespoke_event_handling( ), data: None, }; - outgoing.send_error(request_id, error).await; + outgoing.send_error(request_id.clone(), error).await; return; } }; @@ -1829,6 +1831,7 @@ async fn construct_mcp_tool_call_end_notification( mod tests { use super::*; use crate::CHANNEL_CAPACITY; + use crate::outgoing_message::OutgoingEnvelope; use crate::outgoing_message::OutgoingMessage; use crate::outgoing_message::OutgoingMessageSender; use anyhow::Result; @@ -1856,6 +1859,21 @@ mod tests { Arc::new(Mutex::new(HashMap::new())) } + async fn recv_broadcast_message( + rx: &mut mpsc::Receiver, + ) -> Result { + let envelope = rx + .recv() + .await + .ok_or_else(|| anyhow!("should send one message"))?; + match envelope { + OutgoingEnvelope::Broadcast { message } => Ok(message), + OutgoingEnvelope::ToConnection { connection_id, .. } => { + bail!("unexpected targeted message for connection {connection_id:?}") + } + } + } + #[test] fn file_change_accept_for_session_maps_to_approved_for_session() { let (decision, completion_status) = @@ -1908,10 +1926,7 @@ mod tests { ) .await; - let msg = rx - .recv() - .await - .ok_or_else(|| anyhow!("should send one notification"))?; + let msg = recv_broadcast_message(&mut rx).await?; match msg { OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => { assert_eq!(n.turn.id, event_turn_id); @@ -1950,10 +1965,7 @@ mod tests { ) .await; - let msg = rx - .recv() - .await - .ok_or_else(|| anyhow!("should send one notification"))?; + let msg = recv_broadcast_message(&mut rx).await?; match msg { OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => { assert_eq!(n.turn.id, event_turn_id); @@ -1992,10 +2004,7 @@ mod tests { ) .await; - let msg = rx - .recv() - .await - .ok_or_else(|| anyhow!("should send one notification"))?; + let msg = recv_broadcast_message(&mut rx).await?; match msg { OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => { assert_eq!(n.turn.id, event_turn_id); @@ -2044,10 +2053,7 @@ mod tests { ) .await; - let msg = rx - .recv() - .await - .ok_or_else(|| anyhow!("should send one notification"))?; + let msg = recv_broadcast_message(&mut rx).await?; match msg { OutgoingMessage::AppServerNotification(ServerNotification::TurnPlanUpdated(n)) => { assert_eq!(n.thread_id, conversation_id.to_string()); @@ -2115,10 +2121,7 @@ mod tests { ) .await; - let first = rx - .recv() - .await - .ok_or_else(|| anyhow!("expected usage notification"))?; + let first = recv_broadcast_message(&mut rx).await?; match first { OutgoingMessage::AppServerNotification( ServerNotification::ThreadTokenUsageUpdated(payload), @@ -2134,10 +2137,7 @@ mod tests { other => bail!("unexpected notification: {other:?}"), } - let second = rx - .recv() - .await - .ok_or_else(|| anyhow!("expected rate limit notification"))?; + let second = recv_broadcast_message(&mut rx).await?; match second { OutgoingMessage::AppServerNotification( ServerNotification::AccountRateLimitsUpdated(payload), @@ -2274,10 +2274,7 @@ mod tests { .await; // Verify: A turn 1 - let msg = rx - .recv() - .await - .ok_or_else(|| anyhow!("should send first notification"))?; + let msg = recv_broadcast_message(&mut rx).await?; match msg { OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => { assert_eq!(n.turn.id, a_turn1); @@ -2295,10 +2292,7 @@ mod tests { } // Verify: B turn 1 - let msg = rx - .recv() - .await - .ok_or_else(|| anyhow!("should send second notification"))?; + let msg = recv_broadcast_message(&mut rx).await?; match msg { OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => { assert_eq!(n.turn.id, b_turn1); @@ -2316,10 +2310,7 @@ mod tests { } // Verify: A turn 2 - let msg = rx - .recv() - .await - .ok_or_else(|| anyhow!("should send third notification"))?; + let msg = recv_broadcast_message(&mut rx).await?; match msg { OutgoingMessage::AppServerNotification(ServerNotification::TurnCompleted(n)) => { assert_eq!(n.turn.id, a_turn2); @@ -2485,10 +2476,7 @@ mod tests { ) .await; - let msg = rx - .recv() - .await - .ok_or_else(|| anyhow!("should send one notification"))?; + let msg = recv_broadcast_message(&mut rx).await?; match msg { OutgoingMessage::AppServerNotification(ServerNotification::TurnDiffUpdated( notification, diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 866998a4fd..f96daebc19 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -3,6 +3,8 @@ use crate::error_code::INTERNAL_ERROR_CODE; use crate::error_code::INVALID_REQUEST_ERROR_CODE; use crate::fuzzy_file_search::run_fuzzy_file_search; use crate::models::supported_models; +use crate::outgoing_message::ConnectionId; +use crate::outgoing_message::ConnectionRequestId; use crate::outgoing_message::OutgoingMessageSender; use crate::outgoing_message::OutgoingNotification; use chrono::DateTime; @@ -32,6 +34,10 @@ use codex_app_server_protocol::ConversationGitInfo; use codex_app_server_protocol::ConversationSummary; use codex_app_server_protocol::DynamicToolSpec as ApiDynamicToolSpec; use codex_app_server_protocol::ExecOneOffCommandResponse; +use codex_app_server_protocol::ExperimentalFeature as ApiExperimentalFeature; +use codex_app_server_protocol::ExperimentalFeatureListParams; +use codex_app_server_protocol::ExperimentalFeatureListResponse; +use codex_app_server_protocol::ExperimentalFeatureStage as ApiExperimentalFeatureStage; use codex_app_server_protocol::FeedbackUploadParams; use codex_app_server_protocol::FeedbackUploadResponse; use codex_app_server_protocol::ForkConversationParams; @@ -77,7 +83,6 @@ use codex_app_server_protocol::NewConversationParams; use codex_app_server_protocol::NewConversationResponse; use codex_app_server_protocol::RemoveConversationListenerParams; use codex_app_server_protocol::RemoveConversationSubscriptionResponse; -use codex_app_server_protocol::RequestId; use codex_app_server_protocol::ResumeConversationParams; use codex_app_server_protocol::ResumeConversationResponse; use codex_app_server_protocol::ReviewDelivery as ApiReviewDelivery; @@ -104,6 +109,8 @@ use codex_app_server_protocol::SkillsRemoteWriteResponse; use codex_app_server_protocol::Thread; use codex_app_server_protocol::ThreadArchiveParams; use codex_app_server_protocol::ThreadArchiveResponse; +use codex_app_server_protocol::ThreadCompactStartParams; +use codex_app_server_protocol::ThreadCompactStartResponse; use codex_app_server_protocol::ThreadForkParams; use codex_app_server_protocol::ThreadForkResponse; use codex_app_server_protocol::ThreadItem; @@ -132,12 +139,15 @@ use codex_app_server_protocol::TurnStartParams; use codex_app_server_protocol::TurnStartResponse; use codex_app_server_protocol::TurnStartedNotification; use codex_app_server_protocol::TurnStatus; +use codex_app_server_protocol::TurnSteerParams; +use codex_app_server_protocol::TurnSteerResponse; use codex_app_server_protocol::UserInfoResponse; use codex_app_server_protocol::UserInput as V2UserInput; use codex_app_server_protocol::UserSavedConfig; use codex_app_server_protocol::build_turns_from_event_msgs; use codex_backend_client::Client as BackendClient; use codex_chatgpt::connectors; +use codex_cloud_requirements::cloud_requirements_loader; use codex_core::AuthManager; use codex_core::CodexAuth; use codex_core::CodexThread; @@ -146,9 +156,11 @@ use codex_core::InitialHistory; use codex_core::NewThread; use codex_core::RolloutRecorder; use codex_core::SessionMeta; +use codex_core::SteerInputError; use codex_core::ThreadConfigSnapshot; use codex_core::ThreadManager; use codex_core::ThreadSortKey as CoreThreadSortKey; +use codex_core::auth::AuthMode as CoreAuthMode; use codex_core::auth::CLIENT_ID; use codex_core::auth::login_with_api_key; use codex_core::auth::login_with_chatgpt_auth_tokens; @@ -160,10 +172,13 @@ use codex_core::config::edit::ConfigEditsBuilder; use codex_core::config::types::McpServerTransportConfig; use codex_core::config_loader::CloudRequirementsLoader; use codex_core::default_client::get_codex_user_agent; +use codex_core::default_client::set_default_client_residency_requirement; use codex_core::error::CodexErr; use codex_core::exec::ExecParams; use codex_core::exec_env::create_env; +use codex_core::features::FEATURES; use codex_core::features::Feature; +use codex_core::features::Stage; use codex_core::find_archived_thread_path_by_id_str; use codex_core::find_thread_path_by_id_str; use codex_core::git_info::git_diff_to_remote; @@ -182,7 +197,8 @@ use codex_core::rollout_date_parts; use codex_core::sandboxing::SandboxPermissions; use codex_core::skills::remote::download_remote_skill; use codex_core::skills::remote::list_remote_skills; -use codex_core::state_db::get_state_db; +use codex_core::state_db::StateDbHandle; +use codex_core::state_db::open_if_present; use codex_core::token_data::parse_id_token; use codex_core::windows_sandbox::WindowsSandboxLevelExt; use codex_feedback::CodexFeedback; @@ -216,6 +232,7 @@ use std::io::Error as IoError; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use std::sync::RwLock; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::time::Duration; @@ -232,10 +249,10 @@ use uuid::Uuid; use crate::filters::compute_source_filters; use crate::filters::source_kind_matches; -type PendingInterruptQueue = Vec<(RequestId, ApiVersion)>; +type PendingInterruptQueue = Vec<(ConnectionRequestId, ApiVersion)>; pub(crate) type PendingInterrupts = Arc>>; -pub(crate) type PendingRollbacks = Arc>>; +pub(crate) type PendingRollbacks = Arc>>; /// Per-conversation accumulation of the latest states e.g. error message while a turn runs. #[derive(Default, Clone)] @@ -275,7 +292,7 @@ pub(crate) struct CodexMessageProcessor { codex_linux_sandbox_exe: Option, config: Arc, cli_overrides: Vec<(String, TomlValue)>, - cloud_requirements: CloudRequirementsLoader, + cloud_requirements: Arc>, conversation_listeners: HashMap>, listener_thread_ids_by_subscription: HashMap, active_login: Arc>>, @@ -301,7 +318,7 @@ pub(crate) struct CodexMessageProcessorArgs { pub(crate) codex_linux_sandbox_exe: Option, pub(crate) config: Arc, pub(crate) cli_overrides: Vec<(String, TomlValue)>, - pub(crate) cloud_requirements: CloudRequirementsLoader, + pub(crate) cloud_requirements: Arc>, pub(crate) feedback: CodexFeedback, } @@ -360,9 +377,10 @@ impl CodexMessageProcessor { } async fn load_latest_config(&self) -> Result { + let cloud_requirements = self.current_cloud_requirements(); codex_core::config::ConfigBuilder::default() .cli_overrides(self.cli_overrides.clone()) - .cloud_requirements(self.cloud_requirements.clone()) + .cloud_requirements(cloud_requirements) .build() .await .map_err(|err| JSONRPCErrorError { @@ -372,6 +390,13 @@ impl CodexMessageProcessor { }) } + fn current_cloud_requirements(&self) -> CloudRequirementsLoader { + self.cloud_requirements + .read() + .map(|guard| guard.clone()) + .unwrap_or_default() + } + fn review_request_from_target( target: ApiReviewTarget, ) -> Result<(ReviewRequest, String), JSONRPCErrorError> { @@ -431,90 +456,130 @@ impl CodexMessageProcessor { Ok((review_request, hint)) } - pub async fn process_request(&mut self, request: ClientRequest) { + pub async fn process_request(&mut self, connection_id: ConnectionId, request: ClientRequest) { + let to_connection_request_id = |request_id| ConnectionRequestId { + connection_id, + request_id, + }; + match request { ClientRequest::Initialize { .. } => { panic!("Initialize should be handled in MessageProcessor"); } // === v2 Thread/Turn APIs === ClientRequest::ThreadStart { request_id, params } => { - self.thread_start(request_id, params).await; + self.thread_start(to_connection_request_id(request_id), params) + .await; } ClientRequest::ThreadResume { request_id, params } => { - self.thread_resume(request_id, params).await; + self.thread_resume(to_connection_request_id(request_id), params) + .await; } ClientRequest::ThreadFork { request_id, params } => { - self.thread_fork(request_id, params).await; + self.thread_fork(to_connection_request_id(request_id), params) + .await; } ClientRequest::ThreadArchive { request_id, params } => { - self.thread_archive(request_id, params).await; + self.thread_archive(to_connection_request_id(request_id), params) + .await; } ClientRequest::ThreadSetName { request_id, params } => { - self.thread_set_name(request_id, params).await; + self.thread_set_name(to_connection_request_id(request_id), params) + .await; } ClientRequest::ThreadUnarchive { request_id, params } => { - self.thread_unarchive(request_id, params).await; + self.thread_unarchive(to_connection_request_id(request_id), params) + .await; + } + ClientRequest::ThreadCompactStart { request_id, params } => { + self.thread_compact_start(to_connection_request_id(request_id), params) + .await; } ClientRequest::ThreadRollback { request_id, params } => { - self.thread_rollback(request_id, params).await; + self.thread_rollback(to_connection_request_id(request_id), params) + .await; } ClientRequest::ThreadList { request_id, params } => { - self.thread_list(request_id, params).await; + self.thread_list(to_connection_request_id(request_id), params) + .await; } ClientRequest::ThreadLoadedList { request_id, params } => { - self.thread_loaded_list(request_id, params).await; + self.thread_loaded_list(to_connection_request_id(request_id), params) + .await; } ClientRequest::ThreadRead { request_id, params } => { - self.thread_read(request_id, params).await; + self.thread_read(to_connection_request_id(request_id), params) + .await; } ClientRequest::SkillsList { request_id, params } => { - self.skills_list(request_id, params).await; + self.skills_list(to_connection_request_id(request_id), params) + .await; } ClientRequest::SkillsRemoteRead { request_id, params } => { - self.skills_remote_read(request_id, params).await; + self.skills_remote_read(to_connection_request_id(request_id), params) + .await; } ClientRequest::SkillsRemoteWrite { request_id, params } => { - self.skills_remote_write(request_id, params).await; + self.skills_remote_write(to_connection_request_id(request_id), params) + .await; } ClientRequest::AppsList { request_id, params } => { - self.apps_list(request_id, params).await; + self.apps_list(to_connection_request_id(request_id), params) + .await; } ClientRequest::SkillsConfigWrite { request_id, params } => { - self.skills_config_write(request_id, params).await; + self.skills_config_write(to_connection_request_id(request_id), params) + .await; } ClientRequest::TurnStart { request_id, params } => { - self.turn_start(request_id, params).await; + self.turn_start(to_connection_request_id(request_id), params) + .await; + } + ClientRequest::TurnSteer { request_id, params } => { + self.turn_steer(to_connection_request_id(request_id), params) + .await; } ClientRequest::TurnInterrupt { request_id, params } => { - self.turn_interrupt(request_id, params).await; + self.turn_interrupt(to_connection_request_id(request_id), params) + .await; } ClientRequest::ReviewStart { request_id, params } => { - self.review_start(request_id, params).await; + self.review_start(to_connection_request_id(request_id), params) + .await; } ClientRequest::NewConversation { request_id, params } => { // Do not tokio::spawn() to process new_conversation() // asynchronously because we need to ensure the conversation is // created before processing any subsequent messages. - self.process_new_conversation(request_id, params).await; + self.process_new_conversation(to_connection_request_id(request_id), params) + .await; } ClientRequest::GetConversationSummary { request_id, params } => { - self.get_thread_summary(request_id, params).await; + self.get_thread_summary(to_connection_request_id(request_id), params) + .await; } ClientRequest::ListConversations { request_id, params } => { - self.handle_list_conversations(request_id, params).await; + self.handle_list_conversations(to_connection_request_id(request_id), params) + .await; } ClientRequest::ModelList { request_id, params } => { let outgoing = self.outgoing.clone(); let thread_manager = self.thread_manager.clone(); let config = self.config.clone(); + let request_id = to_connection_request_id(request_id); tokio::spawn(async move { Self::list_models(outgoing, thread_manager, config, request_id, params).await; }); } + ClientRequest::ExperimentalFeatureList { request_id, params } => { + self.experimental_feature_list(to_connection_request_id(request_id), params) + .await; + } ClientRequest::CollaborationModeList { request_id, params } => { let outgoing = self.outgoing.clone(); let thread_manager = self.thread_manager.clone(); + let request_id = to_connection_request_id(request_id); tokio::spawn(async move { Self::list_collaboration_modes(outgoing, thread_manager, request_id, params) @@ -522,109 +587,136 @@ impl CodexMessageProcessor { }); } ClientRequest::MockExperimentalMethod { request_id, params } => { - self.mock_experimental_method(request_id, params).await; + self.mock_experimental_method(to_connection_request_id(request_id), params) + .await; } ClientRequest::McpServerOauthLogin { request_id, params } => { - self.mcp_server_oauth_login(request_id, params).await; + self.mcp_server_oauth_login(to_connection_request_id(request_id), params) + .await; } ClientRequest::McpServerRefresh { request_id, params } => { - self.mcp_server_refresh(request_id, params).await; + self.mcp_server_refresh(to_connection_request_id(request_id), params) + .await; } ClientRequest::McpServerStatusList { request_id, params } => { - self.list_mcp_server_status(request_id, params).await; + self.list_mcp_server_status(to_connection_request_id(request_id), params) + .await; } ClientRequest::LoginAccount { request_id, params } => { - self.login_v2(request_id, params).await; + self.login_v2(to_connection_request_id(request_id), params) + .await; } ClientRequest::LogoutAccount { request_id, params: _, } => { - self.logout_v2(request_id).await; + self.logout_v2(to_connection_request_id(request_id)).await; } ClientRequest::CancelLoginAccount { request_id, params } => { - self.cancel_login_v2(request_id, params).await; + self.cancel_login_v2(to_connection_request_id(request_id), params) + .await; } ClientRequest::GetAccount { request_id, params } => { - self.get_account(request_id, params).await; + self.get_account(to_connection_request_id(request_id), params) + .await; } ClientRequest::ResumeConversation { request_id, params } => { - self.handle_resume_conversation(request_id, params).await; + self.handle_resume_conversation(to_connection_request_id(request_id), params) + .await; } ClientRequest::ForkConversation { request_id, params } => { - self.handle_fork_conversation(request_id, params).await; + self.handle_fork_conversation(to_connection_request_id(request_id), params) + .await; } ClientRequest::ArchiveConversation { request_id, params } => { - self.archive_conversation(request_id, params).await; + self.archive_conversation(to_connection_request_id(request_id), params) + .await; } ClientRequest::SendUserMessage { request_id, params } => { - self.send_user_message(request_id, params).await; + self.send_user_message(to_connection_request_id(request_id), params) + .await; } ClientRequest::SendUserTurn { request_id, params } => { - self.send_user_turn(request_id, params).await; + self.send_user_turn(to_connection_request_id(request_id), params) + .await; } ClientRequest::InterruptConversation { request_id, params } => { - self.interrupt_conversation(request_id, params).await; + self.interrupt_conversation(to_connection_request_id(request_id), params) + .await; } ClientRequest::AddConversationListener { request_id, params } => { - self.add_conversation_listener(request_id, params).await; + self.add_conversation_listener(to_connection_request_id(request_id), params) + .await; } ClientRequest::RemoveConversationListener { request_id, params } => { - self.remove_thread_listener(request_id, params).await; + self.remove_thread_listener(to_connection_request_id(request_id), params) + .await; } ClientRequest::GitDiffToRemote { request_id, params } => { - self.git_diff_to_origin(request_id, params.cwd).await; + self.git_diff_to_origin(to_connection_request_id(request_id), params.cwd) + .await; } ClientRequest::LoginApiKey { request_id, params } => { - self.login_api_key_v1(request_id, params).await; + self.login_api_key_v1(to_connection_request_id(request_id), params) + .await; } ClientRequest::LoginChatGpt { request_id, params: _, } => { - self.login_chatgpt_v1(request_id).await; + self.login_chatgpt_v1(to_connection_request_id(request_id)) + .await; } ClientRequest::CancelLoginChatGpt { request_id, params } => { - self.cancel_login_chatgpt(request_id, params.login_id).await; + self.cancel_login_chatgpt(to_connection_request_id(request_id), params.login_id) + .await; } ClientRequest::LogoutChatGpt { request_id, params: _, } => { - self.logout_v1(request_id).await; + self.logout_v1(to_connection_request_id(request_id)).await; } ClientRequest::GetAuthStatus { request_id, params } => { - self.get_auth_status(request_id, params).await; + self.get_auth_status(to_connection_request_id(request_id), params) + .await; } ClientRequest::GetUserSavedConfig { request_id, params: _, } => { - self.get_user_saved_config(request_id).await; + self.get_user_saved_config(to_connection_request_id(request_id)) + .await; } ClientRequest::SetDefaultModel { request_id, params } => { - self.set_default_model(request_id, params).await; + self.set_default_model(to_connection_request_id(request_id), params) + .await; } ClientRequest::GetUserAgent { request_id, params: _, } => { - self.get_user_agent(request_id).await; + self.get_user_agent(to_connection_request_id(request_id)) + .await; } ClientRequest::UserInfo { request_id, params: _, } => { - self.get_user_info(request_id).await; + self.get_user_info(to_connection_request_id(request_id)) + .await; } ClientRequest::FuzzyFileSearch { request_id, params } => { - self.fuzzy_file_search(request_id, params).await; + self.fuzzy_file_search(to_connection_request_id(request_id), params) + .await; } ClientRequest::OneOffCommandExec { request_id, params } => { - self.exec_one_off_command(request_id, params).await; + self.exec_one_off_command(to_connection_request_id(request_id), params) + .await; } ClientRequest::ExecOneOffCommand { request_id, params } => { - self.exec_one_off_command(request_id, params.into()).await; + self.exec_one_off_command(to_connection_request_id(request_id), params.into()) + .await; } ClientRequest::ConfigRead { .. } | ClientRequest::ConfigValueWrite { .. } @@ -638,15 +730,17 @@ impl CodexMessageProcessor { request_id, params: _, } => { - self.get_account_rate_limits(request_id).await; + self.get_account_rate_limits(to_connection_request_id(request_id)) + .await; } ClientRequest::FeedbackUpload { request_id, params } => { - self.upload_feedback(request_id, params).await; + self.upload_feedback(to_connection_request_id(request_id), params) + .await; } } } - async fn login_v2(&mut self, request_id: RequestId, params: LoginAccountParams) { + async fn login_v2(&mut self, request_id: ConnectionRequestId, params: LoginAccountParams) { match params { LoginAccountParams::ApiKey { api_key } => { self.login_api_key_v2(request_id, LoginApiKeyParams { api_key }) @@ -718,7 +812,11 @@ impl CodexMessageProcessor { } } - async fn login_api_key_v1(&mut self, request_id: RequestId, params: LoginApiKeyParams) { + async fn login_api_key_v1( + &mut self, + request_id: ConnectionRequestId, + params: LoginApiKeyParams, + ) { match self.login_api_key_common(¶ms).await { Ok(()) => { self.outgoing @@ -742,7 +840,11 @@ impl CodexMessageProcessor { } } - async fn login_api_key_v2(&mut self, request_id: RequestId, params: LoginApiKeyParams) { + async fn login_api_key_v2( + &mut self, + request_id: ConnectionRequestId, + params: LoginApiKeyParams, + ) { match self.login_api_key_common(¶ms).await { Ok(()) => { let response = codex_app_server_protocol::LoginAccountResponse::ApiKey {}; @@ -806,7 +908,7 @@ impl CodexMessageProcessor { } // Deprecated in favor of login_chatgpt_v2. - async fn login_chatgpt_v1(&mut self, request_id: RequestId) { + async fn login_chatgpt_v1(&mut self, request_id: ConnectionRequestId) { match self.login_chatgpt_common().await { Ok(opts) => match run_login_server(opts) { Ok(server) => { @@ -829,6 +931,9 @@ impl CodexMessageProcessor { let outgoing_clone = self.outgoing.clone(); let active_login = self.active_login.clone(); let auth_manager = self.auth_manager.clone(); + let cloud_requirements = self.cloud_requirements.clone(); + let chatgpt_base_url = self.config.chatgpt_base_url.clone(); + let cli_overrides = self.cli_overrides.clone(); let auth_url = server.auth_url.clone(); tokio::spawn(async move { let (success, error_msg) = match tokio::time::timeout( @@ -858,6 +963,16 @@ impl CodexMessageProcessor { if success { auth_manager.reload(); + replace_cloud_requirements_loader( + cloud_requirements.as_ref(), + auth_manager.clone(), + chatgpt_base_url, + ); + sync_default_client_residency_requirement( + &cli_overrides, + cloud_requirements.as_ref(), + ) + .await; // Notify clients with the actual current auth mode. let current_auth_method = auth_manager @@ -899,7 +1014,7 @@ impl CodexMessageProcessor { } } - async fn login_chatgpt_v2(&mut self, request_id: RequestId) { + async fn login_chatgpt_v2(&mut self, request_id: ConnectionRequestId) { match self.login_chatgpt_common().await { Ok(opts) => match run_login_server(opts) { Ok(server) => { @@ -922,6 +1037,9 @@ impl CodexMessageProcessor { let outgoing_clone = self.outgoing.clone(); let active_login = self.active_login.clone(); let auth_manager = self.auth_manager.clone(); + let cloud_requirements = self.cloud_requirements.clone(); + let chatgpt_base_url = self.config.chatgpt_base_url.clone(); + let cli_overrides = self.cli_overrides.clone(); let auth_url = server.auth_url.clone(); tokio::spawn(async move { let (success, error_msg) = match tokio::time::timeout( @@ -951,6 +1069,16 @@ impl CodexMessageProcessor { if success { auth_manager.reload(); + replace_cloud_requirements_loader( + cloud_requirements.as_ref(), + auth_manager.clone(), + chatgpt_base_url, + ); + sync_default_client_residency_requirement( + &cli_overrides, + cloud_requirements.as_ref(), + ) + .await; // Notify clients with the actual current auth mode. let current_auth_method = auth_manager @@ -1010,7 +1138,7 @@ impl CodexMessageProcessor { } } - async fn cancel_login_chatgpt(&mut self, request_id: RequestId, login_id: Uuid) { + async fn cancel_login_chatgpt(&mut self, request_id: ConnectionRequestId, login_id: Uuid) { match self.cancel_login_chatgpt_common(login_id).await { Ok(()) => { self.outgoing @@ -1028,7 +1156,11 @@ impl CodexMessageProcessor { } } - async fn cancel_login_v2(&mut self, request_id: RequestId, params: CancelLoginAccountParams) { + async fn cancel_login_v2( + &mut self, + request_id: ConnectionRequestId, + params: CancelLoginAccountParams, + ) { let login_id = params.login_id; match Uuid::parse_str(&login_id) { Ok(uuid) => { @@ -1052,7 +1184,7 @@ impl CodexMessageProcessor { async fn login_chatgpt_auth_tokens( &mut self, - request_id: RequestId, + request_id: ConnectionRequestId, id_token: String, access_token: String, ) { @@ -1118,6 +1250,16 @@ impl CodexMessageProcessor { return; } self.auth_manager.reload(); + replace_cloud_requirements_loader( + self.cloud_requirements.as_ref(), + self.auth_manager.clone(), + self.config.chatgpt_base_url.clone(), + ); + sync_default_client_residency_requirement( + &self.cli_overrides, + self.cloud_requirements.as_ref(), + ) + .await; self.outgoing .send_response(request_id, LoginAccountResponse::ChatgptAuthTokens {}) @@ -1135,7 +1277,7 @@ impl CodexMessageProcessor { .await; let payload_v2 = AccountUpdatedNotification { - auth_mode: self.auth_manager.get_auth_mode(), + auth_mode: self.auth_manager.get_api_auth_mode(), }; self.outgoing .send_server_notification(ServerNotification::AccountUpdated(payload_v2)) @@ -1167,7 +1309,7 @@ impl CodexMessageProcessor { .map(CodexAuth::api_auth_mode)) } - async fn logout_v1(&mut self, request_id: RequestId) { + async fn logout_v1(&mut self, request_id: ConnectionRequestId) { match self.logout_common().await { Ok(current_auth_method) => { self.outgoing @@ -1187,7 +1329,7 @@ impl CodexMessageProcessor { } } - async fn logout_v2(&mut self, request_id: RequestId) { + async fn logout_v2(&mut self, request_id: ConnectionRequestId) { match self.logout_common().await { Ok(current_auth_method) => { self.outgoing @@ -1216,7 +1358,7 @@ impl CodexMessageProcessor { } } - async fn get_auth_status(&self, request_id: RequestId, params: GetAuthStatusParams) { + async fn get_auth_status(&self, request_id: ConnectionRequestId, params: GetAuthStatusParams) { let include_token = params.include_token.unwrap_or(false); let do_refresh = params.refresh_token.unwrap_or(false); @@ -1265,7 +1407,7 @@ impl CodexMessageProcessor { self.outgoing.send_response(request_id, response).await; } - async fn get_account(&self, request_id: RequestId, params: GetAccountParams) { + async fn get_account(&self, request_id: ConnectionRequestId, params: GetAccountParams) { let do_refresh = params.refresh_token; self.refresh_token_if_requested(do_refresh).await; @@ -1283,14 +1425,16 @@ impl CodexMessageProcessor { } let account = match self.auth_manager.auth_cached() { - Some(auth) => Some(match auth { - CodexAuth::ApiKey(_) => Account::ApiKey {}, - CodexAuth::Chatgpt(_) | CodexAuth::ChatgptAuthTokens(_) => { + Some(auth) => match auth.auth_mode() { + CoreAuthMode::ApiKey => Some(Account::ApiKey {}), + CoreAuthMode::Chatgpt => { let email = auth.get_account_email(); let plan_type = auth.account_plan_type(); match (email, plan_type) { - (Some(email), Some(plan_type)) => Account::Chatgpt { email, plan_type }, + (Some(email), Some(plan_type)) => { + Some(Account::Chatgpt { email, plan_type }) + } _ => { let error = JSONRPCErrorError { code: INVALID_REQUEST_ERROR_CODE, @@ -1304,7 +1448,7 @@ impl CodexMessageProcessor { } } } - }), + }, None => None, }; @@ -1315,13 +1459,13 @@ impl CodexMessageProcessor { self.outgoing.send_response(request_id, response).await; } - async fn get_user_agent(&self, request_id: RequestId) { + async fn get_user_agent(&self, request_id: ConnectionRequestId) { let user_agent = get_codex_user_agent(); let response = GetUserAgentResponse { user_agent }; self.outgoing.send_response(request_id, response).await; } - async fn get_account_rate_limits(&self, request_id: RequestId) { + async fn get_account_rate_limits(&self, request_id: ConnectionRequestId) { match self.fetch_account_rate_limits().await { Ok(rate_limits) => { let response = GetAccountRateLimitsResponse { @@ -1369,7 +1513,7 @@ impl CodexMessageProcessor { }) } - async fn get_user_saved_config(&self, request_id: RequestId) { + async fn get_user_saved_config(&self, request_id: ConnectionRequestId) { let service = ConfigService::new_with_defaults(self.config.codex_home.clone()); let user_saved_config: UserSavedConfig = match service.load_user_saved_config().await { Ok(config) => config, @@ -1390,7 +1534,7 @@ impl CodexMessageProcessor { self.outgoing.send_response(request_id, response).await; } - async fn get_user_info(&self, request_id: RequestId) { + async fn get_user_info(&self, request_id: ConnectionRequestId) { // Read alleged user email from cached auth (best-effort; not verified). let alleged_user_email = self .auth_manager @@ -1401,7 +1545,11 @@ impl CodexMessageProcessor { self.outgoing.send_response(request_id, response).await; } - async fn set_default_model(&self, request_id: RequestId, params: SetDefaultModelParams) { + async fn set_default_model( + &self, + request_id: ConnectionRequestId, + params: SetDefaultModelParams, + ) { let SetDefaultModelParams { model, reasoning_effort, @@ -1428,16 +1576,22 @@ impl CodexMessageProcessor { } } - async fn exec_one_off_command(&self, request_id: RequestId, params: CommandExecParams) { + async fn exec_one_off_command( + &self, + request_id: ConnectionRequestId, + params: CommandExecParams, + ) { tracing::debug!("ExecOneOffCommand params: {params:?}"); + let request = request_id.clone(); + if params.command.is_empty() { let error = JSONRPCErrorError { code: INVALID_REQUEST_ERROR_CODE, message: "command must not be empty".to_string(), data: None, }; - self.outgoing.send_error(request_id, error).await; + self.outgoing.send_error(request, error).await; return; } @@ -1468,7 +1622,7 @@ impl CodexMessageProcessor { message: format!("invalid sandbox policy: {err}"), data: None, }; - self.outgoing.send_error(request_id, error).await; + self.outgoing.send_error(request, error).await; return; } }, @@ -1477,8 +1631,9 @@ impl CodexMessageProcessor { let codex_linux_sandbox_exe = self.config.codex_linux_sandbox_exe.clone(); let outgoing = self.outgoing.clone(); - let req_id = request_id; + let request_for_task = request; let sandbox_cwd = self.config.cwd.clone(); + let use_linux_sandbox_bwrap = self.config.features.enabled(Feature::UseLinuxSandboxBwrap); tokio::spawn(async move { match codex_core::exec::process_exec_tool_call( @@ -1486,6 +1641,7 @@ impl CodexMessageProcessor { &effective_policy, sandbox_cwd.as_path(), &codex_linux_sandbox_exe, + use_linux_sandbox_bwrap, None, ) .await @@ -1496,7 +1652,7 @@ impl CodexMessageProcessor { stdout: output.stdout.text, stderr: output.stderr.text, }; - outgoing.send_response(req_id, response).await; + outgoing.send_response(request_for_task, response).await; } Err(err) => { let error = JSONRPCErrorError { @@ -1504,7 +1660,7 @@ impl CodexMessageProcessor { message: format!("exec failed: {err}"), data: None, }; - outgoing.send_error(req_id, error).await; + outgoing.send_error(request_for_task, error).await; } } }); @@ -1512,7 +1668,7 @@ impl CodexMessageProcessor { async fn process_new_conversation( &mut self, - request_id: RequestId, + request_id: ConnectionRequestId, params: NewConversationParams, ) { let NewConversationParams { @@ -1554,11 +1710,12 @@ impl CodexMessageProcessor { ); } + let cloud_requirements = self.current_cloud_requirements(); let config = match derive_config_from_params( &self.cli_overrides, Some(request_overrides), typesafe_overrides, - &self.cloud_requirements, + &cloud_requirements, ) .await { @@ -1612,7 +1769,7 @@ impl CodexMessageProcessor { } } - async fn thread_start(&mut self, request_id: RequestId, params: ThreadStartParams) { + async fn thread_start(&mut self, request_id: ConnectionRequestId, params: ThreadStartParams) { let ThreadStartParams { model, model_provider, @@ -1640,11 +1797,12 @@ impl CodexMessageProcessor { ); typesafe_overrides.ephemeral = ephemeral; + let cloud_requirements = self.current_cloud_requirements(); let config = match derive_config_from_params( &self.cli_overrides, config, typesafe_overrides, - &self.cloud_requirements, + &cloud_requirements, ) .await { @@ -1796,7 +1954,11 @@ impl CodexMessageProcessor { } } - async fn thread_archive(&mut self, request_id: RequestId, params: ThreadArchiveParams) { + async fn thread_archive( + &mut self, + request_id: ConnectionRequestId, + params: ThreadArchiveParams, + ) { // TODO(jif) mostly rewrite this using sqlite after phase 1 let thread_id = match ThreadId::from_string(¶ms.thread_id) { Ok(id) => id, @@ -1846,7 +2008,7 @@ impl CodexMessageProcessor { } } - async fn thread_set_name(&self, request_id: RequestId, params: ThreadSetNameParams) { + async fn thread_set_name(&self, request_id: ConnectionRequestId, params: ThreadSetNameParams) { let ThreadSetNameParams { thread_id, name } = params; let Some(name) = codex_core::util::normalize_thread_name(&name) else { self.send_invalid_request_error( @@ -1876,7 +2038,11 @@ impl CodexMessageProcessor { .await; } - async fn thread_unarchive(&mut self, request_id: RequestId, params: ThreadUnarchiveParams) { + async fn thread_unarchive( + &mut self, + request_id: ConnectionRequestId, + params: ThreadUnarchiveParams, + ) { // TODO(jif) mostly rewrite this using sqlite after phase 1 let thread_id = match ThreadId::from_string(¶ms.thread_id) { Ok(id) => id, @@ -1920,7 +2086,11 @@ impl CodexMessageProcessor { let rollout_path_display = archived_path.display().to_string(); let fallback_provider = self.config.model_provider_id.clone(); - let state_db_ctx = get_state_db(&self.config, None).await; + let state_db_ctx = open_if_present( + &self.config.codex_home, + self.config.model_provider_id.as_str(), + ) + .await; let archived_folder = self .config .codex_home @@ -2049,7 +2219,11 @@ impl CodexMessageProcessor { } } - async fn thread_rollback(&mut self, request_id: RequestId, params: ThreadRollbackParams) { + async fn thread_rollback( + &mut self, + request_id: ConnectionRequestId, + params: ThreadRollbackParams, + ) { let ThreadRollbackParams { thread_id, num_turns, @@ -2069,18 +2243,20 @@ impl CodexMessageProcessor { } }; + let request = request_id.clone(); + { let mut map = self.pending_rollbacks.lock().await; if map.contains_key(&thread_id) { self.send_invalid_request_error( - request_id, + request.clone(), "rollback already in progress for this thread".to_string(), ) .await; return; } - map.insert(thread_id, request_id.clone()); + map.insert(thread_id, request.clone()); } if let Err(err) = thread.submit(Op::ThreadRollback { num_turns }).await { @@ -2089,12 +2265,40 @@ impl CodexMessageProcessor { let mut map = self.pending_rollbacks.lock().await; map.remove(&thread_id); - self.send_internal_error(request_id, format!("failed to start rollback: {err}")) + self.send_internal_error(request, format!("failed to start rollback: {err}")) .await; } } - async fn thread_list(&self, request_id: RequestId, params: ThreadListParams) { + async fn thread_compact_start( + &self, + request_id: ConnectionRequestId, + params: ThreadCompactStartParams, + ) { + let ThreadCompactStartParams { thread_id } = params; + + let (_, thread) = match self.load_thread(&thread_id).await { + Ok(v) => v, + Err(error) => { + self.outgoing.send_error(request_id, error).await; + return; + } + }; + + match thread.submit(Op::Compact).await { + Ok(_) => { + self.outgoing + .send_response(request_id, ThreadCompactStartResponse {}) + .await; + } + Err(err) => { + self.send_internal_error(request_id, format!("failed to start compaction: {err}")) + .await; + } + } + } + + async fn thread_list(&self, request_id: ConnectionRequestId, params: ThreadListParams) { let ThreadListParams { cursor, limit, @@ -2135,7 +2339,11 @@ impl CodexMessageProcessor { self.outgoing.send_response(request_id, response).await; } - async fn thread_loaded_list(&self, request_id: RequestId, params: ThreadLoadedListParams) { + async fn thread_loaded_list( + &self, + request_id: ConnectionRequestId, + params: ThreadLoadedListParams, + ) { let ThreadLoadedListParams { cursor, limit } = params; let mut data = self .thread_manager @@ -2190,7 +2398,7 @@ impl CodexMessageProcessor { self.outgoing.send_response(request_id, response).await; } - async fn thread_read(&mut self, request_id: RequestId, params: ThreadReadParams) { + async fn thread_read(&mut self, request_id: ConnectionRequestId, params: ThreadReadParams) { let ThreadReadParams { thread_id, include_turns, @@ -2205,23 +2413,44 @@ impl CodexMessageProcessor { } }; - let rollout_path = - match find_thread_path_by_id_str(&self.config.codex_home, &thread_uuid.to_string()) - .await - { - Ok(Some(path)) => Some(path), - Ok(None) => None, - Err(err) => { - self.send_invalid_request_error( - request_id, - format!("failed to locate thread id {thread_uuid}: {err}"), - ) - .await; - return; - } - }; + let db_summary = read_summary_from_state_db_by_thread_id(&self.config, thread_uuid).await; + let mut rollout_path = db_summary.as_ref().map(|summary| summary.path.clone()); + if rollout_path.is_none() || include_turns { + rollout_path = + match find_thread_path_by_id_str(&self.config.codex_home, &thread_uuid.to_string()) + .await + { + Ok(Some(path)) => Some(path), + Ok(None) => { + if include_turns { + None + } else { + rollout_path + } + } + Err(err) => { + self.send_invalid_request_error( + request_id, + format!("failed to locate thread id {thread_uuid}: {err}"), + ) + .await; + return; + } + }; + } - let mut thread = if let Some(rollout_path) = rollout_path.as_ref() { + if include_turns && rollout_path.is_none() && db_summary.is_some() { + self.send_internal_error( + request_id, + format!("failed to locate rollout for thread {thread_uuid}"), + ) + .await; + return; + } + + let mut thread = if let Some(summary) = db_summary { + summary_to_thread(summary) + } else if let Some(rollout_path) = rollout_path.as_ref() { let fallback_provider = self.config.model_provider_id.as_str(); match read_summary_from_rollout(rollout_path, fallback_provider).await { Ok(summary) => summary_to_thread(summary), @@ -2306,7 +2535,7 @@ impl CodexMessageProcessor { } } - async fn thread_resume(&mut self, request_id: RequestId, params: ThreadResumeParams) { + async fn thread_resume(&mut self, request_id: ConnectionRequestId, params: ThreadResumeParams) { let ThreadResumeParams { thread_id, history, @@ -2409,12 +2638,13 @@ impl CodexMessageProcessor { ); // Derive a Config using the same logic as new conversation, honoring overrides if provided. + let cloud_requirements = self.current_cloud_requirements(); let config = match derive_config_for_cwd( &self.cli_overrides, request_overrides, typesafe_overrides, history_cwd, - &self.cloud_requirements, + &cloud_requirements, ) .await { @@ -2513,7 +2743,7 @@ impl CodexMessageProcessor { } } - async fn thread_fork(&mut self, request_id: RequestId, params: ThreadForkParams) { + async fn thread_fork(&mut self, request_id: ConnectionRequestId, params: ThreadForkParams) { let ThreadForkParams { thread_id, path, @@ -2527,8 +2757,8 @@ impl CodexMessageProcessor { developer_instructions, } = params; - let rollout_path = if let Some(path) = path { - path + let (rollout_path, source_thread_id) = if let Some(path) = path { + (path, None) } else { let existing_thread_id = match ThreadId::from_string(&thread_id) { Ok(id) => id, @@ -2549,7 +2779,7 @@ impl CodexMessageProcessor { ) .await { - Ok(Some(p)) => p, + Ok(Some(p)) => (p, Some(existing_thread_id)), Ok(None) => { self.send_invalid_request_error( request_id, @@ -2569,14 +2799,9 @@ impl CodexMessageProcessor { } }; - let history_cwd = match read_session_meta_line(&rollout_path).await { - Ok(meta_line) => Some(meta_line.meta.cwd), - Err(err) => { - let rollout_path = rollout_path.display(); - warn!("failed to read session metadata from rollout {rollout_path}: {err}"); - None - } - }; + let history_cwd = + read_history_cwd_from_state_db(&self.config, source_thread_id, rollout_path.as_path()) + .await; // Persist windows sandbox feature. let mut cli_overrides = cli_overrides.unwrap_or_default(); @@ -2602,12 +2827,13 @@ impl CodexMessageProcessor { None, ); // Derive a Config using the same logic as new conversation, honoring overrides if provided. + let cloud_requirements = self.current_cloud_requirements(); let config = match derive_config_for_cwd( &self.cli_overrides, request_overrides, typesafe_overrides, history_cwd, - &self.cloud_requirements, + &cloud_requirements, ) .await { @@ -2722,9 +2948,18 @@ impl CodexMessageProcessor { async fn get_thread_summary( &self, - request_id: RequestId, + request_id: ConnectionRequestId, params: GetConversationSummaryParams, ) { + if let GetConversationSummaryParams::ThreadId { conversation_id } = ¶ms + && let Some(summary) = + read_summary_from_state_db_by_thread_id(&self.config, *conversation_id).await + { + let response = GetConversationSummaryResponse { summary }; + self.outgoing.send_response(request_id, response).await; + return; + } + let path = match params { GetConversationSummaryParams::RolloutPath { rollout_path } => { if rollout_path.is_relative() { @@ -2779,7 +3014,7 @@ impl CodexMessageProcessor { async fn handle_list_conversations( &self, - request_id: RequestId, + request_id: ConnectionRequestId, params: ListConversationsParams, ) { let ListConversationsParams { @@ -2849,6 +3084,11 @@ impl CodexMessageProcessor { let fallback_provider = self.config.model_provider_id.clone(); let (allowed_sources_vec, source_kind_filter) = compute_source_filters(source_kinds); let allowed_sources = allowed_sources_vec.as_slice(); + let state_db_ctx = open_if_present( + &self.config.codex_home, + self.config.model_provider_id.as_str(), + ) + .await; while remaining > 0 { let page_size = remaining.min(THREAD_LIST_MAX_LIMIT); @@ -2886,31 +3126,26 @@ impl CodexMessageProcessor { })? }; - let mut filtered = page - .items - .into_iter() - .filter_map(|it| { - let updated_at = it.updated_at.clone(); - let session_meta_line = it.head.first().and_then(|first| { - serde_json::from_value::(first.clone()).ok() - })?; - extract_conversation_summary( - it.path, - &it.head, - &session_meta_line.meta, - session_meta_line.git.as_ref(), - fallback_provider.as_str(), - updated_at, - ) - }) - .filter(|summary| { - source_kind_filter - .as_ref() - .is_none_or(|filter| source_kind_matches(&summary.source, filter)) - }) - .collect::>(); - if filtered.len() > remaining { - filtered.truncate(remaining); + let mut filtered = Vec::with_capacity(page.items.len()); + for it in page.items { + let Some(summary) = summary_from_thread_list_item( + it, + fallback_provider.as_str(), + state_db_ctx.as_ref(), + ) + .await + else { + continue; + }; + if source_kind_filter + .as_ref() + .is_none_or(|filter| source_kind_matches(&summary.source, filter)) + { + filtered.push(summary); + if filtered.len() >= remaining { + break; + } + } } items.extend(filtered); remaining = requested_page_size.saturating_sub(items.len()); @@ -2947,7 +3182,7 @@ impl CodexMessageProcessor { outgoing: Arc, thread_manager: Arc, config: Arc, - request_id: RequestId, + request_id: ConnectionRequestId, params: ModelListParams, ) { let ModelListParams { limit, cursor } = params; @@ -3010,7 +3245,7 @@ impl CodexMessageProcessor { async fn list_collaboration_modes( outgoing: Arc, thread_manager: Arc, - request_id: RequestId, + request_id: ConnectionRequestId, params: CollaborationModeListParams, ) { let CollaborationModeListParams {} = params; @@ -3019,9 +3254,119 @@ impl CodexMessageProcessor { outgoing.send_response(request_id, response).await; } + async fn experimental_feature_list( + &self, + request_id: ConnectionRequestId, + params: ExperimentalFeatureListParams, + ) { + let ExperimentalFeatureListParams { cursor, limit } = params; + let config = match self.load_latest_config().await { + Ok(config) => config, + Err(error) => { + self.outgoing.send_error(request_id, error).await; + return; + } + }; + + let data = FEATURES + .iter() + .map(|spec| { + let (stage, display_name, description, announcement) = match spec.stage { + Stage::Experimental { + name, + menu_description, + announcement, + } => ( + ApiExperimentalFeatureStage::Beta, + Some(name.to_string()), + Some(menu_description.to_string()), + Some(announcement.to_string()), + ), + Stage::UnderDevelopment => ( + ApiExperimentalFeatureStage::UnderDevelopment, + None, + None, + None, + ), + Stage::Stable => (ApiExperimentalFeatureStage::Stable, None, None, None), + Stage::Deprecated => { + (ApiExperimentalFeatureStage::Deprecated, None, None, None) + } + Stage::Removed => (ApiExperimentalFeatureStage::Removed, None, None, None), + }; + + ApiExperimentalFeature { + name: spec.key.to_string(), + stage, + display_name, + description, + announcement, + enabled: config.features.enabled(spec.id), + default_enabled: spec.default_enabled, + } + }) + .collect::>(); + + let total = data.len(); + if total == 0 { + self.outgoing + .send_response( + request_id, + ExperimentalFeatureListResponse { + data: Vec::new(), + next_cursor: None, + }, + ) + .await; + return; + } + + // Clamp to 1 so limit=0 cannot return a non-advancing page. + let effective_limit = limit.unwrap_or(total as u32).max(1) as usize; + let effective_limit = effective_limit.min(total); + let start = match cursor { + Some(cursor) => match cursor.parse::() { + Ok(idx) => idx, + Err(_) => { + self.send_invalid_request_error( + request_id, + format!("invalid cursor: {cursor}"), + ) + .await; + return; + } + }, + None => 0, + }; + + if start > total { + self.send_invalid_request_error( + request_id, + format!("cursor {start} exceeds total feature flags {total}"), + ) + .await; + return; + } + + let end = start.saturating_add(effective_limit).min(total); + let data = data[start..end].to_vec(); + let next_cursor = if end < total { + Some(end.to_string()) + } else { + None + }; + + self.outgoing + .send_response( + request_id, + ExperimentalFeatureListResponse { data, next_cursor }, + ) + .await; + } + async fn mock_experimental_method( &self, - request_id: RequestId, + request_id: ConnectionRequestId, params: MockExperimentalMethodParams, ) { let MockExperimentalMethodParams { value } = params; @@ -3029,7 +3374,7 @@ impl CodexMessageProcessor { self.outgoing.send_response(request_id, response).await; } - async fn mcp_server_refresh(&self, request_id: RequestId, _params: Option<()>) { + async fn mcp_server_refresh(&self, request_id: ConnectionRequestId, _params: Option<()>) { let config = match self.load_latest_config().await { Ok(config) => config, Err(error) => { @@ -3082,7 +3427,7 @@ impl CodexMessageProcessor { async fn mcp_server_oauth_login( &self, - request_id: RequestId, + request_id: ConnectionRequestId, params: McpServerOauthLoginParams, ) { let config = match self.load_latest_config().await { @@ -3179,26 +3524,28 @@ impl CodexMessageProcessor { async fn list_mcp_server_status( &self, - request_id: RequestId, + request_id: ConnectionRequestId, params: ListMcpServerStatusParams, ) { + let request = request_id.clone(); + let outgoing = Arc::clone(&self.outgoing); let config = match self.load_latest_config().await { Ok(config) => config, Err(error) => { - self.outgoing.send_error(request_id, error).await; + self.outgoing.send_error(request, error).await; return; } }; tokio::spawn(async move { - Self::list_mcp_server_status_task(outgoing, request_id, params, config).await; + Self::list_mcp_server_status_task(outgoing, request, params, config).await; }); } async fn list_mcp_server_status_task( outgoing: Arc, - request_id: RequestId, + request_id: ConnectionRequestId, params: ListMcpServerStatusParams, config: Config, ) { @@ -3281,7 +3628,7 @@ impl CodexMessageProcessor { async fn handle_resume_conversation( &self, - request_id: RequestId, + request_id: ConnectionRequestId, params: ResumeConversationParams, ) { let ResumeConversationParams { @@ -3407,12 +3754,13 @@ impl CodexMessageProcessor { ), }; + let cloud_requirements = self.current_cloud_requirements(); let config = match derive_config_for_cwd( &self.cli_overrides, request_overrides, typesafe_overrides, history_cwd, - &self.cloud_requirements, + &cloud_requirements, ) .await { @@ -3488,7 +3836,7 @@ impl CodexMessageProcessor { async fn handle_fork_conversation( &self, - request_id: RequestId, + request_id: ConnectionRequestId, params: ForkConversationParams, ) { let ForkConversationParams { @@ -3498,13 +3846,13 @@ impl CodexMessageProcessor { } = params; // Derive a Config using the same logic as new conversation, honoring overrides if provided. - let rollout_path = if let Some(path) = path { - path + let (rollout_path, source_thread_id) = if let Some(path) = path { + (path, None) } else if let Some(conversation_id) = conversation_id { match find_thread_path_by_id_str(&self.config.codex_home, &conversation_id.to_string()) .await { - Ok(Some(found_path)) => found_path, + Ok(Some(found_path)) => (found_path, Some(conversation_id)), Ok(None) => { self.send_invalid_request_error( request_id, @@ -3531,14 +3879,9 @@ impl CodexMessageProcessor { return; }; - let history_cwd = match read_session_meta_line(&rollout_path).await { - Ok(meta_line) => Some(meta_line.meta.cwd), - Err(err) => { - let rollout_path = rollout_path.display(); - warn!("failed to read session metadata from rollout {rollout_path}: {err}"); - None - } - }; + let history_cwd = + read_history_cwd_from_state_db(&self.config, source_thread_id, rollout_path.as_path()) + .await; let (typesafe_overrides, request_overrides) = match overrides { Some(overrides) => { @@ -3596,12 +3939,13 @@ impl CodexMessageProcessor { ), }; + let cloud_requirements = self.current_cloud_requirements(); let config = match derive_config_for_cwd( &self.cli_overrides, request_overrides, typesafe_overrides, history_cwd, - &self.cloud_requirements, + &cloud_requirements, ) .await { @@ -3688,7 +4032,7 @@ impl CodexMessageProcessor { self.outgoing.send_response(request_id, response).await; } - async fn send_invalid_request_error(&self, request_id: RequestId, message: String) { + async fn send_invalid_request_error(&self, request_id: ConnectionRequestId, message: String) { let error = JSONRPCErrorError { code: INVALID_REQUEST_ERROR_CODE, message, @@ -3697,7 +4041,7 @@ impl CodexMessageProcessor { self.outgoing.send_error(request_id, error).await; } - async fn send_internal_error(&self, request_id: RequestId, message: String) { + async fn send_internal_error(&self, request_id: ConnectionRequestId, message: String) { let error = JSONRPCErrorError { code: INTERNAL_ERROR_CODE, message, @@ -3708,7 +4052,7 @@ impl CodexMessageProcessor { async fn archive_conversation( &mut self, - request_id: RequestId, + request_id: ConnectionRequestId, params: ArchiveConversationParams, ) { let ArchiveConversationParams { @@ -3825,7 +4169,11 @@ impl CodexMessageProcessor { } if state_db_ctx.is_none() { - state_db_ctx = get_state_db(&self.config, None).await; + state_db_ctx = open_if_present( + &self.config.codex_home, + self.config.model_provider_id.as_str(), + ) + .await; } // Move the rollout file to archived. @@ -3853,7 +4201,11 @@ impl CodexMessageProcessor { }) } - async fn send_user_message(&self, request_id: RequestId, params: SendUserMessageParams) { + async fn send_user_message( + &self, + request_id: ConnectionRequestId, + params: SendUserMessageParams, + ) { let SendUserMessageParams { conversation_id, items, @@ -3897,7 +4249,7 @@ impl CodexMessageProcessor { .await; } - async fn send_user_turn(&self, request_id: RequestId, params: SendUserTurnParams) { + async fn send_user_turn(&self, request_id: ConnectionRequestId, params: SendUserTurnParams) { let SendUserTurnParams { conversation_id, items, @@ -3955,7 +4307,7 @@ impl CodexMessageProcessor { .await; } - async fn apps_list(&self, request_id: RequestId, params: AppsListParams) { + async fn apps_list(&self, request_id: ConnectionRequestId, params: AppsListParams) { let AppsListParams { cursor, limit } = params; let config = match self.load_latest_config().await { Ok(config) => config, @@ -4040,7 +4392,7 @@ impl CodexMessageProcessor { .await; } - async fn skills_list(&self, request_id: RequestId, params: SkillsListParams) { + async fn skills_list(&self, request_id: ConnectionRequestId, params: SkillsListParams) { let SkillsListParams { cwds, force_reload } = params; let cwds = if cwds.is_empty() { vec![self.config.cwd.clone()] @@ -4065,7 +4417,11 @@ impl CodexMessageProcessor { .await; } - async fn skills_remote_read(&self, request_id: RequestId, _params: SkillsRemoteReadParams) { + async fn skills_remote_read( + &self, + request_id: ConnectionRequestId, + _params: SkillsRemoteReadParams, + ) { match list_remote_skills(&self.config).await { Ok(skills) => { let data = skills @@ -4090,7 +4446,11 @@ impl CodexMessageProcessor { } } - async fn skills_remote_write(&self, request_id: RequestId, params: SkillsRemoteWriteParams) { + async fn skills_remote_write( + &self, + request_id: ConnectionRequestId, + params: SkillsRemoteWriteParams, + ) { let SkillsRemoteWriteParams { hazelnut_id, is_preload, @@ -4120,7 +4480,11 @@ impl CodexMessageProcessor { } } - async fn skills_config_write(&self, request_id: RequestId, params: SkillsConfigWriteParams) { + async fn skills_config_write( + &self, + request_id: ConnectionRequestId, + params: SkillsConfigWriteParams, + ) { let SkillsConfigWriteParams { path, enabled } = params; let edits = vec![ConfigEdit::SetSkillConfig { path, enabled }]; let result = ConfigEditsBuilder::new(&self.config.codex_home) @@ -4153,7 +4517,7 @@ impl CodexMessageProcessor { async fn interrupt_conversation( &mut self, - request_id: RequestId, + request_id: ConnectionRequestId, params: InterruptConversationParams, ) { let InterruptConversationParams { conversation_id } = params; @@ -4167,19 +4531,21 @@ impl CodexMessageProcessor { return; }; + let request = request_id.clone(); + // Record the pending interrupt so we can reply when TurnAborted arrives. { let mut map = self.pending_interrupts.lock().await; map.entry(conversation_id) .or_default() - .push((request_id, ApiVersion::V1)); + .push((request, ApiVersion::V1)); } // Submit the interrupt; we'll respond upon TurnAborted. let _ = conversation.submit(Op::Interrupt).await; } - async fn turn_start(&self, request_id: RequestId, params: TurnStartParams) { + async fn turn_start(&self, request_id: ConnectionRequestId, params: TurnStartParams) { let (_, thread) = match self.load_thread(¶ms.thread_id).await { Ok(v) => v, Err(error) => { @@ -4261,6 +4627,63 @@ impl CodexMessageProcessor { } } + async fn turn_steer(&self, request_id: ConnectionRequestId, params: TurnSteerParams) { + let (_, thread) = match self.load_thread(¶ms.thread_id).await { + Ok(v) => v, + Err(error) => { + self.outgoing.send_error(request_id, error).await; + return; + } + }; + + if params.expected_turn_id.is_empty() { + self.send_invalid_request_error( + request_id, + "expectedTurnId must not be empty".to_string(), + ) + .await; + return; + } + + let mapped_items: Vec = params + .input + .into_iter() + .map(V2UserInput::into_core) + .collect(); + + match thread + .steer_input(mapped_items, Some(¶ms.expected_turn_id)) + .await + { + Ok(turn_id) => { + let response = TurnSteerResponse { turn_id }; + self.outgoing.send_response(request_id, response).await; + } + Err(err) => { + let (code, message) = match err { + SteerInputError::NoActiveTurn(_) => ( + INVALID_REQUEST_ERROR_CODE, + "no active turn to steer".to_string(), + ), + SteerInputError::ExpectedTurnMismatch { expected, actual } => ( + INVALID_REQUEST_ERROR_CODE, + format!("expected active turn id `{expected}` but found `{actual}`"), + ), + SteerInputError::EmptyInput => ( + INVALID_REQUEST_ERROR_CODE, + "input must not be empty".to_string(), + ), + }; + let error = JSONRPCErrorError { + code, + message, + data: None, + }; + self.outgoing.send_error(request_id, error).await; + } + } + } + fn build_review_turn(turn_id: String, display_text: &str) -> Turn { let items = if display_text.is_empty() { Vec::new() @@ -4285,7 +4708,7 @@ impl CodexMessageProcessor { async fn emit_review_started( &self, - request_id: &RequestId, + request_id: &ConnectionRequestId, turn: Turn, parent_thread_id: String, review_thread_id: String, @@ -4309,7 +4732,7 @@ impl CodexMessageProcessor { async fn start_inline_review( &self, - request_id: &RequestId, + request_id: &ConnectionRequestId, parent_thread: Arc, review_request: ReviewRequest, display_text: &str, @@ -4339,7 +4762,7 @@ impl CodexMessageProcessor { async fn start_detached_review( &mut self, - request_id: &RequestId, + request_id: &ConnectionRequestId, parent_thread_id: ThreadId, review_request: ReviewRequest, display_text: &str, @@ -4431,7 +4854,7 @@ impl CodexMessageProcessor { Ok(()) } - async fn review_start(&mut self, request_id: RequestId, params: ReviewStartParams) { + async fn review_start(&mut self, request_id: ConnectionRequestId, params: ReviewStartParams) { let ReviewStartParams { thread_id, target, @@ -4485,7 +4908,11 @@ impl CodexMessageProcessor { } } - async fn turn_interrupt(&mut self, request_id: RequestId, params: TurnInterruptParams) { + async fn turn_interrupt( + &mut self, + request_id: ConnectionRequestId, + params: TurnInterruptParams, + ) { let TurnInterruptParams { thread_id, .. } = params; let (thread_uuid, thread) = match self.load_thread(&thread_id).await { @@ -4496,12 +4923,14 @@ impl CodexMessageProcessor { } }; + let request = request_id.clone(); + // Record the pending interrupt so we can reply when TurnAborted arrives. { let mut map = self.pending_interrupts.lock().await; map.entry(thread_uuid) .or_default() - .push((request_id, ApiVersion::V2)); + .push((request, ApiVersion::V2)); } // Submit the interrupt; we'll respond upon TurnAborted. @@ -4510,7 +4939,7 @@ impl CodexMessageProcessor { async fn add_conversation_listener( &mut self, - request_id: RequestId, + request_id: ConnectionRequestId, params: AddConversationListenerParams, ) { let AddConversationListenerParams { @@ -4533,7 +4962,7 @@ impl CodexMessageProcessor { async fn remove_thread_listener( &mut self, - request_id: RequestId, + request_id: ConnectionRequestId, params: RemoveConversationListenerParams, ) { let RemoveConversationListenerParams { subscription_id } = params; @@ -4663,7 +5092,7 @@ impl CodexMessageProcessor { Ok(subscription_id) } - async fn git_diff_to_origin(&self, request_id: RequestId, cwd: PathBuf) { + async fn git_diff_to_origin(&self, request_id: ConnectionRequestId, cwd: PathBuf) { let diff = git_diff_to_remote(&cwd).await; match diff { Some(value) => { @@ -4684,7 +5113,11 @@ impl CodexMessageProcessor { } } - async fn fuzzy_file_search(&mut self, request_id: RequestId, params: FuzzyFileSearchParams) { + async fn fuzzy_file_search( + &mut self, + request_id: ConnectionRequestId, + params: FuzzyFileSearchParams, + ) { let FuzzyFileSearchParams { query, roots, @@ -4724,7 +5157,7 @@ impl CodexMessageProcessor { self.outgoing.send_response(request_id, response).await; } - async fn upload_feedback(&self, request_id: RequestId, params: FeedbackUploadParams) { + async fn upload_feedback(&self, request_id: ConnectionRequestId, params: FeedbackUploadParams) { if !self.config.feedback_enabled { let error = JSONRPCErrorError { code: INVALID_REQUEST_ERROR_CODE, @@ -4913,6 +5346,41 @@ fn validate_dynamic_tools( Ok(()) } +fn replace_cloud_requirements_loader( + cloud_requirements: &RwLock, + auth_manager: Arc, + chatgpt_base_url: String, +) { + let loader = cloud_requirements_loader(auth_manager, chatgpt_base_url); + if let Ok(mut guard) = cloud_requirements.write() { + *guard = loader; + } else { + warn!("failed to update cloud requirements loader"); + } +} + +async fn sync_default_client_residency_requirement( + cli_overrides: &[(String, TomlValue)], + cloud_requirements: &RwLock, +) { + let loader = cloud_requirements + .read() + .map(|guard| guard.clone()) + .unwrap_or_default(); + match codex_core::config::ConfigBuilder::default() + .cli_overrides(cli_overrides.to_vec()) + .cloud_requirements(loader) + .build() + .await + { + Ok(config) => set_default_client_residency_requirement(config.enforce_residency.value()), + Err(err) => warn!( + error = %err, + "failed to sync default client residency requirement after auth refresh" + ), + } +} + /// Derive the effective [`Config`] by layering three override sources. /// /// Precedence (lowest to highest): @@ -4975,6 +5443,168 @@ async fn derive_config_for_cwd( .await } +async fn read_history_cwd_from_state_db( + config: &Config, + thread_id: Option, + rollout_path: &Path, +) -> Option { + if let Some(state_db_ctx) = + open_if_present(&config.codex_home, config.model_provider_id.as_str()).await + && let Some(thread_id) = thread_id + && let Ok(Some(metadata)) = state_db_ctx.get_thread(thread_id).await + { + return Some(metadata.cwd); + } + + match read_session_meta_line(rollout_path).await { + Ok(meta_line) => Some(meta_line.meta.cwd), + Err(err) => { + let rollout_path = rollout_path.display(); + warn!("failed to read session metadata from rollout {rollout_path}: {err}"); + None + } + } +} + +async fn read_summary_from_state_db_by_thread_id( + config: &Config, + thread_id: ThreadId, +) -> Option { + let state_db_ctx = open_if_present(&config.codex_home, config.model_provider_id.as_str()).await; + read_summary_from_state_db_context_by_thread_id(state_db_ctx.as_ref(), thread_id).await +} + +async fn read_summary_from_state_db_context_by_thread_id( + state_db_ctx: Option<&StateDbHandle>, + thread_id: ThreadId, +) -> Option { + let state_db_ctx = state_db_ctx?; + + let metadata = match state_db_ctx.get_thread(thread_id).await { + Ok(Some(metadata)) => metadata, + Ok(None) | Err(_) => return None, + }; + Some(summary_from_state_db_metadata( + metadata.id, + metadata.rollout_path, + metadata.first_user_message, + metadata + .created_at + .to_rfc3339_opts(SecondsFormat::Secs, true), + metadata + .updated_at + .to_rfc3339_opts(SecondsFormat::Secs, true), + metadata.model_provider, + metadata.cwd, + metadata.cli_version, + metadata.source, + metadata.git_sha, + metadata.git_branch, + metadata.git_origin_url, + )) +} + +async fn summary_from_thread_list_item( + it: codex_core::ThreadItem, + fallback_provider: &str, + state_db_ctx: Option<&StateDbHandle>, +) -> Option { + if let Some(thread_id) = it.thread_id { + let timestamp = it.created_at.clone(); + let updated_at = it.updated_at.clone().or_else(|| timestamp.clone()); + let model_provider = it + .model_provider + .clone() + .unwrap_or_else(|| fallback_provider.to_string()); + let cwd = it.cwd?; + let cli_version = it.cli_version.unwrap_or_default(); + let source = it + .source + .unwrap_or(codex_protocol::protocol::SessionSource::Unknown); + return Some(ConversationSummary { + conversation_id: thread_id, + path: it.path, + preview: it.first_user_message.unwrap_or_default(), + timestamp, + updated_at, + model_provider, + cwd, + cli_version, + source, + git_info: if it.git_sha.is_none() + && it.git_branch.is_none() + && it.git_origin_url.is_none() + { + None + } else { + Some(ConversationGitInfo { + sha: it.git_sha, + branch: it.git_branch, + origin_url: it.git_origin_url, + }) + }, + }); + } + if let Some(thread_id) = thread_id_from_rollout_path(it.path.as_path()) { + return read_summary_from_state_db_context_by_thread_id(state_db_ctx, thread_id).await; + } + None +} + +fn thread_id_from_rollout_path(path: &Path) -> Option { + let file_name = path.file_name()?.to_str()?; + let stem = file_name.strip_suffix(".jsonl")?; + if stem.len() < 37 { + return None; + } + let uuid_start = stem.len().saturating_sub(36); + if !stem[..uuid_start].ends_with('-') { + return None; + } + ThreadId::from_string(&stem[uuid_start..]).ok() +} + +#[allow(clippy::too_many_arguments)] +fn summary_from_state_db_metadata( + conversation_id: ThreadId, + path: PathBuf, + first_user_message: Option, + timestamp: String, + updated_at: String, + model_provider: String, + cwd: PathBuf, + cli_version: String, + source: String, + git_sha: Option, + git_branch: Option, + git_origin_url: Option, +) -> ConversationSummary { + let preview = first_user_message.unwrap_or_default(); + let source = serde_json::from_value(serde_json::Value::String(source)) + .unwrap_or(codex_protocol::protocol::SessionSource::Unknown); + let git_info = if git_sha.is_none() && git_branch.is_none() && git_origin_url.is_none() { + None + } else { + Some(ConversationGitInfo { + sha: git_sha, + branch: git_branch, + origin_url: git_origin_url, + }) + }; + ConversationSummary { + conversation_id, + path, + preview, + timestamp: Some(timestamp), + updated_at: Some(updated_at), + model_provider, + cwd, + cli_version, + source, + git_info, + } +} + pub(crate) async fn read_summary_from_rollout( path: &Path, fallback_provider: &str, diff --git a/codex-rs/app-server/src/config_api.rs b/codex-rs/app-server/src/config_api.rs index 5a43d6e528..e1f27be0b5 100644 --- a/codex-rs/app-server/src/config_api.rs +++ b/codex-rs/app-server/src/config_api.rs @@ -19,11 +19,16 @@ use codex_core::config_loader::ResidencyRequirement as CoreResidencyRequirement; use codex_core::config_loader::SandboxModeRequirement as CoreSandboxModeRequirement; use serde_json::json; use std::path::PathBuf; +use std::sync::Arc; +use std::sync::RwLock; use toml::Value as TomlValue; #[derive(Clone)] pub(crate) struct ConfigApi { - service: ConfigService, + codex_home: PathBuf, + cli_overrides: Vec<(String, TomlValue)>, + loader_overrides: LoaderOverrides, + cloud_requirements: Arc>, } impl ConfigApi { @@ -31,30 +36,42 @@ impl ConfigApi { codex_home: PathBuf, cli_overrides: Vec<(String, TomlValue)>, loader_overrides: LoaderOverrides, - cloud_requirements: CloudRequirementsLoader, + cloud_requirements: Arc>, ) -> Self { Self { - service: ConfigService::new( - codex_home, - cli_overrides, - loader_overrides, - cloud_requirements, - ), + codex_home, + cli_overrides, + loader_overrides, + cloud_requirements, } } + fn config_service(&self) -> ConfigService { + let cloud_requirements = self + .cloud_requirements + .read() + .map(|guard| guard.clone()) + .unwrap_or_default(); + ConfigService::new( + self.codex_home.clone(), + self.cli_overrides.clone(), + self.loader_overrides.clone(), + cloud_requirements, + ) + } + pub(crate) async fn read( &self, params: ConfigReadParams, ) -> Result { - self.service.read(params).await.map_err(map_error) + self.config_service().read(params).await.map_err(map_error) } pub(crate) async fn config_requirements_read( &self, ) -> Result { let requirements = self - .service + .config_service() .read_requirements() .await .map_err(map_error)? @@ -67,14 +84,20 @@ impl ConfigApi { &self, params: ConfigValueWriteParams, ) -> Result { - self.service.write_value(params).await.map_err(map_error) + self.config_service() + .write_value(params) + .await + .map_err(map_error) } pub(crate) async fn batch_write( &self, params: ConfigBatchWriteParams, ) -> Result { - self.service.batch_write(params).await.map_err(map_error) + self.config_service() + .batch_write(params) + .await + .map_err(map_error) } } diff --git a/codex-rs/app-server/src/dynamic_tools.rs b/codex-rs/app-server/src/dynamic_tools.rs index a1b424d0ee..ed284452b4 100644 --- a/codex-rs/app-server/src/dynamic_tools.rs +++ b/codex-rs/app-server/src/dynamic_tools.rs @@ -1,5 +1,6 @@ use codex_app_server_protocol::DynamicToolCallResponse; use codex_core::CodexThread; +use codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem as CoreDynamicToolCallOutputContentItem; use codex_protocol::dynamic_tools::DynamicToolResponse as CoreDynamicToolResponse; use codex_protocol::protocol::Op; use std::sync::Arc; @@ -17,8 +18,9 @@ pub(crate) async fn on_call_response( Err(err) => { error!("request failed: {err:?}"); let fallback = CoreDynamicToolResponse { - call_id: call_id.clone(), - output: "dynamic tool request failed".to_string(), + content_items: vec![CoreDynamicToolCallOutputContentItem::InputText { + text: "dynamic tool request failed".to_string(), + }], success: false, }; if let Err(err) = conversation @@ -37,14 +39,25 @@ pub(crate) async fn on_call_response( let response = serde_json::from_value::(value).unwrap_or_else(|err| { error!("failed to deserialize DynamicToolCallResponse: {err}"); DynamicToolCallResponse { - output: "dynamic tool response was invalid".to_string(), + content_items: vec![ + codex_app_server_protocol::DynamicToolCallOutputContentItem::InputText { + text: "dynamic tool response was invalid".to_string(), + }, + ], success: false, } }); + + let DynamicToolCallResponse { + content_items, + success, + } = response; let response = CoreDynamicToolResponse { - call_id: call_id.clone(), - output: response.output, - success: response.success, + content_items: content_items + .into_iter() + .map(CoreDynamicToolCallOutputContentItem::from) + .collect(), + success, }; if let Err(err) = conversation .submit(Op::DynamicToolResponse { diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index f7d108f55f..c558233413 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -8,14 +8,24 @@ use codex_core::config::ConfigBuilder; use codex_core::config_loader::CloudRequirementsLoader; use codex_core::config_loader::ConfigLayerStackOrdering; use codex_core::config_loader::LoaderOverrides; +use std::collections::HashMap; use std::io::ErrorKind; use std::io::Result as IoResult; use std::path::PathBuf; +use std::sync::Arc; use crate::message_processor::MessageProcessor; use crate::message_processor::MessageProcessorArgs; -use crate::outgoing_message::OutgoingMessage; +use crate::outgoing_message::ConnectionId; +use crate::outgoing_message::OutgoingEnvelope; use crate::outgoing_message::OutgoingMessageSender; +use crate::transport::CHANNEL_CAPACITY; +use crate::transport::ConnectionState; +use crate::transport::TransportEvent; +use crate::transport::has_initialized_connections; +use crate::transport::route_outgoing_envelope; +use crate::transport::start_stdio_connection; +use crate::transport::start_websocket_acceptor; use codex_app_server_protocol::ConfigLayerSource; use codex_app_server_protocol::ConfigWarningNotification; use codex_app_server_protocol::JSONRPCMessage; @@ -26,13 +36,9 @@ use codex_core::check_execpolicy_for_warnings; use codex_core::config_loader::ConfigLoadError; use codex_core::config_loader::TextRange as CoreTextRange; use codex_feedback::CodexFeedback; -use tokio::io::AsyncBufReadExt; -use tokio::io::AsyncWriteExt; -use tokio::io::BufReader; -use tokio::io::{self}; use tokio::sync::mpsc; +use tokio::task::JoinHandle; use toml::Value as TomlValue; -use tracing::debug; use tracing::error; use tracing::info; use tracing::warn; @@ -51,11 +57,9 @@ mod fuzzy_file_search; mod message_processor; mod models; mod outgoing_message; +mod transport; -/// Size of the bounded channels used to communicate between tasks. The value -/// is a balance between throughput and memory usage – 128 messages should be -/// plenty for an interactive CLI. -const CHANNEL_CAPACITY: usize = 128; +pub use crate::transport::AppServerTransport; fn config_warning_from_error( summary: impl Into, @@ -173,32 +177,39 @@ pub async fn run_main( loader_overrides: LoaderOverrides, default_analytics_enabled: bool, ) -> IoResult<()> { - // Set up channels. - let (incoming_tx, mut incoming_rx) = mpsc::channel::(CHANNEL_CAPACITY); - let (outgoing_tx, mut outgoing_rx) = mpsc::channel::(CHANNEL_CAPACITY); + run_main_with_transport( + codex_linux_sandbox_exe, + cli_config_overrides, + loader_overrides, + default_analytics_enabled, + AppServerTransport::Stdio, + ) + .await +} - // Task: read from stdin, push to `incoming_tx`. - let stdin_reader_handle = tokio::spawn({ - async move { - let stdin = io::stdin(); - let reader = BufReader::new(stdin); - let mut lines = reader.lines(); +pub async fn run_main_with_transport( + codex_linux_sandbox_exe: Option, + cli_config_overrides: CliConfigOverrides, + loader_overrides: LoaderOverrides, + default_analytics_enabled: bool, + transport: AppServerTransport, +) -> IoResult<()> { + let (transport_event_tx, mut transport_event_rx) = + mpsc::channel::(CHANNEL_CAPACITY); + let (outgoing_tx, mut outgoing_rx) = mpsc::channel::(CHANNEL_CAPACITY); - while let Some(line) = lines.next_line().await.unwrap_or_default() { - match serde_json::from_str::(&line) { - Ok(msg) => { - if incoming_tx.send(msg).await.is_err() { - // Receiver gone – nothing left to do. - break; - } - } - Err(e) => error!("Failed to deserialize JSONRPCMessage: {e}"), - } - } - - debug!("stdin reader finished (EOF)"); + let mut stdio_handles = Vec::>::new(); + let mut websocket_accept_handle = None; + match transport { + AppServerTransport::Stdio => { + start_stdio_connection(transport_event_tx.clone(), &mut stdio_handles).await?; } - }); + AppServerTransport::WebSocket { bind_address } => { + websocket_accept_handle = + Some(start_websocket_acceptor(bind_address, transport_event_tx.clone()).await?); + } + } + let shutdown_when_no_connections = matches!(transport, AppServerTransport::Stdio); // Parse CLI overrides once and derive the base Config eagerly so later // components do not need to work with raw TOML values. @@ -327,15 +338,14 @@ pub async fn run_main( } } - // Task: process incoming messages. let processor_handle = tokio::spawn({ - let outgoing_message_sender = OutgoingMessageSender::new(outgoing_tx); + let outgoing_message_sender = Arc::new(OutgoingMessageSender::new(outgoing_tx)); let cli_overrides: Vec<(String, TomlValue)> = cli_kv_overrides.clone(); let loader_overrides = loader_overrides_for_config_api; let mut processor = MessageProcessor::new(MessageProcessorArgs { outgoing: outgoing_message_sender, codex_linux_sandbox_exe, - config: std::sync::Arc::new(config), + config: Arc::new(config), cli_overrides, loader_overrides, cloud_requirements: cloud_requirements.clone(), @@ -343,25 +353,65 @@ pub async fn run_main( config_warnings, }); let mut thread_created_rx = processor.thread_created_receiver(); + let mut connections = HashMap::::new(); async move { let mut listen_for_threads = true; loop { tokio::select! { - msg = incoming_rx.recv() => { - let Some(msg) = msg else { + event = transport_event_rx.recv() => { + let Some(event) = event else { break; }; - match msg { - JSONRPCMessage::Request(r) => processor.process_request(r).await, - JSONRPCMessage::Response(r) => processor.process_response(r).await, - JSONRPCMessage::Notification(n) => processor.process_notification(n).await, - JSONRPCMessage::Error(e) => processor.process_error(e).await, + match event { + TransportEvent::ConnectionOpened { connection_id, writer } => { + connections.insert(connection_id, ConnectionState::new(writer)); + } + TransportEvent::ConnectionClosed { connection_id } => { + connections.remove(&connection_id); + if shutdown_when_no_connections && connections.is_empty() { + break; + } + } + TransportEvent::IncomingMessage { connection_id, message } => { + match message { + JSONRPCMessage::Request(request) => { + let Some(connection_state) = connections.get_mut(&connection_id) else { + warn!("dropping request from unknown connection: {:?}", connection_id); + continue; + }; + processor + .process_request( + connection_id, + request, + &mut connection_state.session, + ) + .await; + } + JSONRPCMessage::Response(response) => { + processor.process_response(response).await; + } + JSONRPCMessage::Notification(notification) => { + processor.process_notification(notification).await; + } + JSONRPCMessage::Error(err) => { + processor.process_error(err).await; + } + } + } } } + envelope = outgoing_rx.recv() => { + let Some(envelope) = envelope else { + break; + }; + route_outgoing_envelope(&mut connections, envelope).await; + } created = thread_created_rx.recv(), if listen_for_threads => { match created { Ok(thread_id) => { - processor.try_attach_thread_listener(thread_id).await; + if has_initialized_connections(&connections) { + processor.try_attach_thread_listener(thread_id).await; + } } Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => { // TODO(jif) handle lag. @@ -382,33 +432,17 @@ pub async fn run_main( } }); - // Task: write outgoing messages to stdout. - let stdout_writer_handle = tokio::spawn(async move { - let mut stdout = io::stdout(); - while let Some(outgoing_message) = outgoing_rx.recv().await { - let Ok(value) = serde_json::to_value(outgoing_message) else { - error!("Failed to convert OutgoingMessage to JSON value"); - continue; - }; - match serde_json::to_string(&value) { - Ok(mut json) => { - json.push('\n'); - if let Err(e) = stdout.write_all(json.as_bytes()).await { - error!("Failed to write to stdout: {e}"); - break; - } - } - Err(e) => error!("Failed to serialize JSONRPCMessage: {e}"), - } - } + drop(transport_event_tx); - info!("stdout writer exited (channel closed)"); - }); + let _ = processor_handle.await; - // Wait for all tasks to finish. The typical exit path is the stdin reader - // hitting EOF which, once it drops `incoming_tx`, propagates shutdown to - // the processor and then to the stdout task. - let _ = tokio::join!(stdin_reader_handle, processor_handle, stdout_writer_handle); + if let Some(handle) = websocket_accept_handle { + handle.abort(); + } + + for handle in stdio_handles { + let _ = handle.await; + } Ok(()) } diff --git a/codex-rs/app-server/src/main.rs b/codex-rs/app-server/src/main.rs index 71d6dc338c..40dec1dc80 100644 --- a/codex-rs/app-server/src/main.rs +++ b/codex-rs/app-server/src/main.rs @@ -1,4 +1,6 @@ -use codex_app_server::run_main; +use clap::Parser; +use codex_app_server::AppServerTransport; +use codex_app_server::run_main_with_transport; use codex_arg0::arg0_dispatch_or_else; use codex_common::CliConfigOverrides; use codex_core::config_loader::LoaderOverrides; @@ -8,19 +10,34 @@ use std::path::PathBuf; // managed config file without writing to /etc. const MANAGED_CONFIG_PATH_ENV_VAR: &str = "CODEX_APP_SERVER_MANAGED_CONFIG_PATH"; +#[derive(Debug, Parser)] +struct AppServerArgs { + /// Transport endpoint URL. Supported values: `stdio://` (default), + /// `ws://IP:PORT`. + #[arg( + long = "listen", + value_name = "URL", + default_value = AppServerTransport::DEFAULT_LISTEN_URL + )] + listen: AppServerTransport, +} + fn main() -> anyhow::Result<()> { arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move { + let args = AppServerArgs::parse(); let managed_config_path = managed_config_path_from_debug_env(); let loader_overrides = LoaderOverrides { managed_config_path, ..Default::default() }; + let transport = args.listen; - run_main( + run_main_with_transport( codex_linux_sandbox_exe, CliConfigOverrides::default(), loader_overrides, false, + transport, ) .await?; Ok(()) diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index db6275dcaf..2646e5f0a5 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -1,12 +1,13 @@ use std::path::PathBuf; use std::sync::Arc; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; +use std::sync::RwLock; use crate::codex_message_processor::CodexMessageProcessor; use crate::codex_message_processor::CodexMessageProcessorArgs; use crate::config_api::ConfigApi; use crate::error_code::INVALID_REQUEST_ERROR_CODE; +use crate::outgoing_message::ConnectionId; +use crate::outgoing_message::ConnectionRequestId; use crate::outgoing_message::OutgoingMessageSender; use async_trait::async_trait; use codex_app_server_protocol::ChatgptAuthTokensRefreshParams; @@ -25,7 +26,6 @@ use codex_app_server_protocol::JSONRPCErrorError; use codex_app_server_protocol::JSONRPCNotification; use codex_app_server_protocol::JSONRPCRequest; use codex_app_server_protocol::JSONRPCResponse; -use codex_app_server_protocol::RequestId; use codex_app_server_protocol::ServerNotification; use codex_app_server_protocol::ServerRequestPayload; use codex_app_server_protocol::experimental_required_message; @@ -110,13 +110,17 @@ pub(crate) struct MessageProcessor { codex_message_processor: CodexMessageProcessor, config_api: ConfigApi, config: Arc, - initialized: bool, - experimental_api_enabled: Arc, - config_warnings: Vec, + config_warnings: Arc>, +} + +#[derive(Debug, Default)] +pub(crate) struct ConnectionSessionState { + pub(crate) initialized: bool, + experimental_api_enabled: bool, } pub(crate) struct MessageProcessorArgs { - pub(crate) outgoing: OutgoingMessageSender, + pub(crate) outgoing: Arc, pub(crate) codex_linux_sandbox_exe: Option, pub(crate) config: Arc, pub(crate) cli_overrides: Vec<(String, TomlValue)>, @@ -140,8 +144,6 @@ impl MessageProcessor { feedback, config_warnings, } = args; - let outgoing = Arc::new(outgoing); - let experimental_api_enabled = Arc::new(AtomicBool::new(false)); let auth_manager = AuthManager::shared( config.codex_home.clone(), false, @@ -156,6 +158,7 @@ impl MessageProcessor { auth_manager.clone(), SessionSource::VSCode, )); + let cloud_requirements = Arc::new(RwLock::new(cloud_requirements)); let codex_message_processor = CodexMessageProcessor::new(CodexMessageProcessorArgs { auth_manager, thread_manager, @@ -178,14 +181,20 @@ impl MessageProcessor { codex_message_processor, config_api, config, - initialized: false, - experimental_api_enabled, - config_warnings, + config_warnings: Arc::new(config_warnings), } } - pub(crate) async fn process_request(&mut self, request: JSONRPCRequest) { - let request_id = request.id.clone(); + pub(crate) async fn process_request( + &mut self, + connection_id: ConnectionId, + request: JSONRPCRequest, + session: &mut ConnectionSessionState, + ) { + let request_id = ConnectionRequestId { + connection_id, + request_id: request.id.clone(), + }; let request_json = match serde_json::to_value(&request) { Ok(request_json) => request_json, Err(err) => { @@ -216,7 +225,11 @@ impl MessageProcessor { // Handle Initialize internally so CodexMessageProcessor does not have to concern // itself with the `initialized` bool. ClientRequest::Initialize { request_id, params } => { - if self.initialized { + let request_id = ConnectionRequestId { + connection_id, + request_id, + }; + if session.initialized { let error = JSONRPCErrorError { code: INVALID_REQUEST_ERROR_CODE, message: "Already initialized".to_string(), @@ -225,12 +238,16 @@ impl MessageProcessor { self.outgoing.send_error(request_id, error).await; return; } else { - let experimental_api_enabled = params + // TODO(maxj): Revisit capability scoping for `experimental_api_enabled`. + // Current behavior is per-connection. Reviewer feedback notes this can + // create odd cross-client behavior (for example dynamic tool calls on a + // shared thread when another connected client did not opt into + // experimental API). Proposed direction is instance-global first-write-wins + // with initialize-time mismatch rejection. + session.experimental_api_enabled = params .capabilities .as_ref() .is_some_and(|cap| cap.experimental_api); - self.experimental_api_enabled - .store(experimental_api_enabled, Ordering::Relaxed); let ClientInfo { name, title: _title, @@ -246,7 +263,7 @@ impl MessageProcessor { ), data: None, }; - self.outgoing.send_error(request_id, error).await; + self.outgoing.send_error(request_id.clone(), error).await; return; } SetOriginatorError::AlreadyInitialized => { @@ -267,22 +284,20 @@ impl MessageProcessor { let response = InitializeResponse { user_agent }; self.outgoing.send_response(request_id, response).await; - self.initialized = true; - if !self.config_warnings.is_empty() { - for notification in self.config_warnings.drain(..) { - self.outgoing - .send_server_notification(ServerNotification::ConfigWarning( - notification, - )) - .await; - } + session.initialized = true; + for notification in self.config_warnings.iter().cloned() { + self.outgoing + .send_server_notification(ServerNotification::ConfigWarning( + notification, + )) + .await; } return; } } _ => { - if !self.initialized { + if !session.initialized { let error = JSONRPCErrorError { code: INVALID_REQUEST_ERROR_CODE, message: "Not initialized".to_string(), @@ -295,7 +310,7 @@ impl MessageProcessor { } if let Some(reason) = codex_request.experimental_reason() - && !self.experimental_api_enabled.load(Ordering::Relaxed) + && !session.experimental_api_enabled { let error = JSONRPCErrorError { code: INVALID_REQUEST_ERROR_CODE, @@ -308,22 +323,49 @@ impl MessageProcessor { match codex_request { ClientRequest::ConfigRead { request_id, params } => { - self.handle_config_read(request_id, params).await; + self.handle_config_read( + ConnectionRequestId { + connection_id, + request_id, + }, + params, + ) + .await; } ClientRequest::ConfigValueWrite { request_id, params } => { - self.handle_config_value_write(request_id, params).await; + self.handle_config_value_write( + ConnectionRequestId { + connection_id, + request_id, + }, + params, + ) + .await; } ClientRequest::ConfigBatchWrite { request_id, params } => { - self.handle_config_batch_write(request_id, params).await; + self.handle_config_batch_write( + ConnectionRequestId { + connection_id, + request_id, + }, + params, + ) + .await; } ClientRequest::ConfigRequirementsRead { request_id, params: _, } => { - self.handle_config_requirements_read(request_id).await; + self.handle_config_requirements_read(ConnectionRequestId { + connection_id, + request_id, + }) + .await; } other => { - self.codex_message_processor.process_request(other).await; + self.codex_message_processor + .process_request(connection_id, other) + .await; } } } @@ -339,9 +381,6 @@ impl MessageProcessor { } pub(crate) async fn try_attach_thread_listener(&mut self, thread_id: ThreadId) { - if !self.initialized { - return; - } self.codex_message_processor .try_attach_thread_listener(thread_id) .await; @@ -360,7 +399,7 @@ impl MessageProcessor { self.outgoing.notify_client_error(err.id, err.error).await; } - async fn handle_config_read(&self, request_id: RequestId, params: ConfigReadParams) { + async fn handle_config_read(&self, request_id: ConnectionRequestId, params: ConfigReadParams) { match self.config_api.read(params).await { Ok(response) => self.outgoing.send_response(request_id, response).await, Err(error) => self.outgoing.send_error(request_id, error).await, @@ -369,7 +408,7 @@ impl MessageProcessor { async fn handle_config_value_write( &self, - request_id: RequestId, + request_id: ConnectionRequestId, params: ConfigValueWriteParams, ) { match self.config_api.write_value(params).await { @@ -380,7 +419,7 @@ impl MessageProcessor { async fn handle_config_batch_write( &self, - request_id: RequestId, + request_id: ConnectionRequestId, params: ConfigBatchWriteParams, ) { match self.config_api.batch_write(params).await { @@ -389,7 +428,7 @@ impl MessageProcessor { } } - async fn handle_config_requirements_read(&self, request_id: RequestId) { + async fn handle_config_requirements_read(&self, request_id: ConnectionRequestId) { match self.config_api.config_requirements_read().await { Ok(response) => self.outgoing.send_response(request_id, response).await, Err(error) => self.outgoing.send_error(request_id, error).await, diff --git a/codex-rs/app-server/src/models.rs b/codex-rs/app-server/src/models.rs index 133dc73cc0..350b86f92a 100644 --- a/codex-rs/app-server/src/models.rs +++ b/codex-rs/app-server/src/models.rs @@ -22,6 +22,7 @@ fn model_from_preset(preset: ModelPreset) -> Model { Model { id: preset.id.to_string(), model: preset.model.to_string(), + upgrade: preset.upgrade.map(|upgrade| upgrade.id), display_name: preset.display_name.to_string(), description: preset.description.to_string(), supported_reasoning_efforts: reasoning_efforts_from_preset( diff --git a/codex-rs/app-server/src/outgoing_message.rs b/codex-rs/app-server/src/outgoing_message.rs index be89775d86..a5219dc2dc 100644 --- a/codex-rs/app-server/src/outgoing_message.rs +++ b/codex-rs/app-server/src/outgoing_message.rs @@ -19,17 +19,39 @@ use crate::error_code::INTERNAL_ERROR_CODE; #[cfg(test)] use codex_protocol::account::PlanType; +/// Stable identifier for a transport connection. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub(crate) struct ConnectionId(pub(crate) u64); + +/// Stable identifier for a client request scoped to a transport connection. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub(crate) struct ConnectionRequestId { + pub(crate) connection_id: ConnectionId, + pub(crate) request_id: RequestId, +} + +#[derive(Debug, Clone)] +pub(crate) enum OutgoingEnvelope { + ToConnection { + connection_id: ConnectionId, + message: OutgoingMessage, + }, + Broadcast { + message: OutgoingMessage, + }, +} + /// Sends messages to the client and manages request callbacks. pub(crate) struct OutgoingMessageSender { - next_request_id: AtomicI64, - sender: mpsc::Sender, + next_server_request_id: AtomicI64, + sender: mpsc::Sender, request_id_to_callback: Mutex>>, } impl OutgoingMessageSender { - pub(crate) fn new(sender: mpsc::Sender) -> Self { + pub(crate) fn new(sender: mpsc::Sender) -> Self { Self { - next_request_id: AtomicI64::new(0), + next_server_request_id: AtomicI64::new(0), sender, request_id_to_callback: Mutex::new(HashMap::new()), } @@ -47,7 +69,7 @@ impl OutgoingMessageSender { &self, request: ServerRequestPayload, ) -> (RequestId, oneshot::Receiver) { - let id = RequestId::Integer(self.next_request_id.fetch_add(1, Ordering::Relaxed)); + let id = RequestId::Integer(self.next_server_request_id.fetch_add(1, Ordering::Relaxed)); let outgoing_message_id = id.clone(); let (tx_approve, rx_approve) = oneshot::channel(); { @@ -57,7 +79,13 @@ impl OutgoingMessageSender { let outgoing_message = OutgoingMessage::Request(request.request_with_id(outgoing_message_id.clone())); - if let Err(err) = self.sender.send(outgoing_message).await { + if let Err(err) = self + .sender + .send(OutgoingEnvelope::Broadcast { + message: outgoing_message, + }) + .await + { warn!("failed to send request {outgoing_message_id:?} to client: {err:?}"); let mut request_id_to_callback = self.request_id_to_callback.lock().await; request_id_to_callback.remove(&outgoing_message_id); @@ -107,17 +135,31 @@ impl OutgoingMessageSender { entry.is_some() } - pub(crate) async fn send_response(&self, id: RequestId, response: T) { + pub(crate) async fn send_response( + &self, + request_id: ConnectionRequestId, + response: T, + ) { match serde_json::to_value(response) { Ok(result) => { - let outgoing_message = OutgoingMessage::Response(OutgoingResponse { id, result }); - if let Err(err) = self.sender.send(outgoing_message).await { + let outgoing_message = OutgoingMessage::Response(OutgoingResponse { + id: request_id.request_id, + result, + }); + if let Err(err) = self + .sender + .send(OutgoingEnvelope::ToConnection { + connection_id: request_id.connection_id, + message: outgoing_message, + }) + .await + { warn!("failed to send response to client: {err:?}"); } } Err(err) => { self.send_error( - id, + request_id, JSONRPCErrorError { code: INTERNAL_ERROR_CODE, message: format!("failed to serialize response: {err}"), @@ -132,7 +174,9 @@ impl OutgoingMessageSender { pub(crate) async fn send_server_notification(&self, notification: ServerNotification) { if let Err(err) = self .sender - .send(OutgoingMessage::AppServerNotification(notification)) + .send(OutgoingEnvelope::Broadcast { + message: OutgoingMessage::AppServerNotification(notification), + }) .await { warn!("failed to send server notification to client: {err:?}"); @@ -143,14 +187,34 @@ impl OutgoingMessageSender { /// [`OutgoingMessage::Notification`] should be removed. pub(crate) async fn send_notification(&self, notification: OutgoingNotification) { let outgoing_message = OutgoingMessage::Notification(notification); - if let Err(err) = self.sender.send(outgoing_message).await { + if let Err(err) = self + .sender + .send(OutgoingEnvelope::Broadcast { + message: outgoing_message, + }) + .await + { warn!("failed to send notification to client: {err:?}"); } } - pub(crate) async fn send_error(&self, id: RequestId, error: JSONRPCErrorError) { - let outgoing_message = OutgoingMessage::Error(OutgoingError { id, error }); - if let Err(err) = self.sender.send(outgoing_message).await { + pub(crate) async fn send_error( + &self, + request_id: ConnectionRequestId, + error: JSONRPCErrorError, + ) { + let outgoing_message = OutgoingMessage::Error(OutgoingError { + id: request_id.request_id, + error, + }); + if let Err(err) = self + .sender + .send(OutgoingEnvelope::ToConnection { + connection_id: request_id.connection_id, + message: outgoing_message, + }) + .await + { warn!("failed to send error to client: {err:?}"); } } @@ -190,6 +254,8 @@ pub(crate) struct OutgoingError { #[cfg(test)] mod tests { + use std::time::Duration; + use codex_app_server_protocol::AccountLoginCompletedNotification; use codex_app_server_protocol::AccountRateLimitsUpdatedNotification; use codex_app_server_protocol::AccountUpdatedNotification; @@ -200,6 +266,7 @@ mod tests { use codex_app_server_protocol::RateLimitWindow; use pretty_assertions::assert_eq; use serde_json::json; + use tokio::time::timeout; use uuid::Uuid; use super::*; @@ -336,4 +403,75 @@ mod tests { "ensure the notification serializes correctly" ); } + + #[tokio::test] + async fn send_response_routes_to_target_connection() { + let (tx, mut rx) = mpsc::channel::(4); + let outgoing = OutgoingMessageSender::new(tx); + let request_id = ConnectionRequestId { + connection_id: ConnectionId(42), + request_id: RequestId::Integer(7), + }; + + outgoing + .send_response(request_id.clone(), json!({ "ok": true })) + .await; + + let envelope = timeout(Duration::from_secs(1), rx.recv()) + .await + .expect("should receive envelope before timeout") + .expect("channel should contain one message"); + + match envelope { + OutgoingEnvelope::ToConnection { + connection_id, + message, + } => { + assert_eq!(connection_id, ConnectionId(42)); + let OutgoingMessage::Response(response) = message else { + panic!("expected response message"); + }; + assert_eq!(response.id, request_id.request_id); + assert_eq!(response.result, json!({ "ok": true })); + } + other => panic!("expected targeted response envelope, got: {other:?}"), + } + } + + #[tokio::test] + async fn send_error_routes_to_target_connection() { + let (tx, mut rx) = mpsc::channel::(4); + let outgoing = OutgoingMessageSender::new(tx); + let request_id = ConnectionRequestId { + connection_id: ConnectionId(9), + request_id: RequestId::Integer(3), + }; + let error = JSONRPCErrorError { + code: INTERNAL_ERROR_CODE, + message: "boom".to_string(), + data: None, + }; + + outgoing.send_error(request_id.clone(), error.clone()).await; + + let envelope = timeout(Duration::from_secs(1), rx.recv()) + .await + .expect("should receive envelope before timeout") + .expect("channel should contain one message"); + + match envelope { + OutgoingEnvelope::ToConnection { + connection_id, + message, + } => { + assert_eq!(connection_id, ConnectionId(9)); + let OutgoingMessage::Error(outgoing_error) = message else { + panic!("expected error message"); + }; + assert_eq!(outgoing_error.id, RequestId::Integer(3)); + assert_eq!(outgoing_error.error, error); + } + other => panic!("expected targeted error envelope, got: {other:?}"), + } + } } diff --git a/codex-rs/app-server/src/transport.rs b/codex-rs/app-server/src/transport.rs new file mode 100644 index 0000000000..52f30055d0 --- /dev/null +++ b/codex-rs/app-server/src/transport.rs @@ -0,0 +1,424 @@ +use crate::message_processor::ConnectionSessionState; +use crate::outgoing_message::ConnectionId; +use crate::outgoing_message::OutgoingEnvelope; +use crate::outgoing_message::OutgoingMessage; +use codex_app_server_protocol::JSONRPCMessage; +use futures::SinkExt; +use futures::StreamExt; +use std::collections::HashMap; +use std::io::ErrorKind; +use std::io::Result as IoResult; +use std::net::SocketAddr; +use std::str::FromStr; +use std::sync::Arc; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering; +use tokio::io::AsyncBufReadExt; +use tokio::io::AsyncWriteExt; +use tokio::io::BufReader; +use tokio::io::{self}; +use tokio::net::TcpListener; +use tokio::net::TcpStream; +use tokio::sync::mpsc; +use tokio::task::JoinHandle; +use tokio_tungstenite::accept_async; +use tokio_tungstenite::tungstenite::Message as WebSocketMessage; +use tracing::debug; +use tracing::error; +use tracing::info; +use tracing::warn; + +/// Size of the bounded channels used to communicate between tasks. The value +/// is a balance between throughput and memory usage - 128 messages should be +/// plenty for an interactive CLI. +pub(crate) const CHANNEL_CAPACITY: usize = 128; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum AppServerTransport { + Stdio, + WebSocket { bind_address: SocketAddr }, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum AppServerTransportParseError { + UnsupportedListenUrl(String), + InvalidWebSocketListenUrl(String), +} + +impl std::fmt::Display for AppServerTransportParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AppServerTransportParseError::UnsupportedListenUrl(listen_url) => write!( + f, + "unsupported --listen URL `{listen_url}`; expected `stdio://` or `ws://IP:PORT`" + ), + AppServerTransportParseError::InvalidWebSocketListenUrl(listen_url) => write!( + f, + "invalid websocket --listen URL `{listen_url}`; expected `ws://IP:PORT`" + ), + } + } +} + +impl std::error::Error for AppServerTransportParseError {} + +impl AppServerTransport { + pub const DEFAULT_LISTEN_URL: &'static str = "stdio://"; + + pub fn from_listen_url(listen_url: &str) -> Result { + if listen_url == Self::DEFAULT_LISTEN_URL { + return Ok(Self::Stdio); + } + + if let Some(socket_addr) = listen_url.strip_prefix("ws://") { + let bind_address = socket_addr.parse::().map_err(|_| { + AppServerTransportParseError::InvalidWebSocketListenUrl(listen_url.to_string()) + })?; + return Ok(Self::WebSocket { bind_address }); + } + + Err(AppServerTransportParseError::UnsupportedListenUrl( + listen_url.to_string(), + )) + } +} + +impl FromStr for AppServerTransport { + type Err = AppServerTransportParseError; + + fn from_str(s: &str) -> Result { + Self::from_listen_url(s) + } +} + +#[derive(Debug)] +pub(crate) enum TransportEvent { + ConnectionOpened { + connection_id: ConnectionId, + writer: mpsc::Sender, + }, + ConnectionClosed { + connection_id: ConnectionId, + }, + IncomingMessage { + connection_id: ConnectionId, + message: JSONRPCMessage, + }, +} + +pub(crate) struct ConnectionState { + pub(crate) writer: mpsc::Sender, + pub(crate) session: ConnectionSessionState, +} + +impl ConnectionState { + pub(crate) fn new(writer: mpsc::Sender) -> Self { + Self { + writer, + session: ConnectionSessionState::default(), + } + } +} + +pub(crate) async fn start_stdio_connection( + transport_event_tx: mpsc::Sender, + stdio_handles: &mut Vec>, +) -> IoResult<()> { + let connection_id = ConnectionId(0); + let (writer_tx, mut writer_rx) = mpsc::channel::(CHANNEL_CAPACITY); + transport_event_tx + .send(TransportEvent::ConnectionOpened { + connection_id, + writer: writer_tx, + }) + .await + .map_err(|_| std::io::Error::new(ErrorKind::BrokenPipe, "processor unavailable"))?; + + let transport_event_tx_for_reader = transport_event_tx.clone(); + stdio_handles.push(tokio::spawn(async move { + let stdin = io::stdin(); + let reader = BufReader::new(stdin); + let mut lines = reader.lines(); + + loop { + match lines.next_line().await { + Ok(Some(line)) => { + if !forward_incoming_message( + &transport_event_tx_for_reader, + connection_id, + &line, + ) + .await + { + break; + } + } + Ok(None) => break, + Err(err) => { + error!("Failed reading stdin: {err}"); + break; + } + } + } + + let _ = transport_event_tx_for_reader + .send(TransportEvent::ConnectionClosed { connection_id }) + .await; + debug!("stdin reader finished (EOF)"); + })); + + stdio_handles.push(tokio::spawn(async move { + let mut stdout = io::stdout(); + while let Some(outgoing_message) = writer_rx.recv().await { + let Some(mut json) = serialize_outgoing_message(outgoing_message) else { + continue; + }; + json.push('\n'); + if let Err(err) = stdout.write_all(json.as_bytes()).await { + error!("Failed to write to stdout: {err}"); + break; + } + } + info!("stdout writer exited (channel closed)"); + })); + + Ok(()) +} + +pub(crate) async fn start_websocket_acceptor( + bind_address: SocketAddr, + transport_event_tx: mpsc::Sender, +) -> IoResult> { + let listener = TcpListener::bind(bind_address).await?; + let local_addr = listener.local_addr()?; + info!("app-server websocket listening on ws://{local_addr}"); + + let connection_counter = Arc::new(AtomicU64::new(1)); + Ok(tokio::spawn(async move { + loop { + match listener.accept().await { + Ok((stream, _peer_addr)) => { + let connection_id = + ConnectionId(connection_counter.fetch_add(1, Ordering::Relaxed)); + let transport_event_tx_for_connection = transport_event_tx.clone(); + tokio::spawn(async move { + run_websocket_connection( + connection_id, + stream, + transport_event_tx_for_connection, + ) + .await; + }); + } + Err(err) => { + error!("failed to accept websocket connection: {err}"); + } + } + } + })) +} + +async fn run_websocket_connection( + connection_id: ConnectionId, + stream: TcpStream, + transport_event_tx: mpsc::Sender, +) { + let websocket_stream = match accept_async(stream).await { + Ok(stream) => stream, + Err(err) => { + warn!("failed to complete websocket handshake: {err}"); + return; + } + }; + + let (writer_tx, mut writer_rx) = mpsc::channel::(CHANNEL_CAPACITY); + if transport_event_tx + .send(TransportEvent::ConnectionOpened { + connection_id, + writer: writer_tx, + }) + .await + .is_err() + { + return; + } + + let (mut websocket_writer, mut websocket_reader) = websocket_stream.split(); + loop { + tokio::select! { + outgoing_message = writer_rx.recv() => { + let Some(outgoing_message) = outgoing_message else { + break; + }; + let Some(json) = serialize_outgoing_message(outgoing_message) else { + continue; + }; + if websocket_writer.send(WebSocketMessage::Text(json.into())).await.is_err() { + break; + } + } + incoming_message = websocket_reader.next() => { + match incoming_message { + Some(Ok(WebSocketMessage::Text(text))) => { + if !forward_incoming_message(&transport_event_tx, connection_id, &text).await { + break; + } + } + Some(Ok(WebSocketMessage::Ping(payload))) => { + if websocket_writer.send(WebSocketMessage::Pong(payload)).await.is_err() { + break; + } + } + Some(Ok(WebSocketMessage::Pong(_))) => {} + Some(Ok(WebSocketMessage::Close(_))) | None => break, + Some(Ok(WebSocketMessage::Binary(_))) => { + warn!("dropping unsupported binary websocket message"); + } + Some(Ok(WebSocketMessage::Frame(_))) => {} + Some(Err(err)) => { + warn!("websocket receive error: {err}"); + break; + } + } + } + } + } + + let _ = transport_event_tx + .send(TransportEvent::ConnectionClosed { connection_id }) + .await; +} + +async fn forward_incoming_message( + transport_event_tx: &mpsc::Sender, + connection_id: ConnectionId, + payload: &str, +) -> bool { + match serde_json::from_str::(payload) { + Ok(message) => transport_event_tx + .send(TransportEvent::IncomingMessage { + connection_id, + message, + }) + .await + .is_ok(), + Err(err) => { + error!("Failed to deserialize JSONRPCMessage: {err}"); + true + } + } +} + +fn serialize_outgoing_message(outgoing_message: OutgoingMessage) -> Option { + let value = match serde_json::to_value(outgoing_message) { + Ok(value) => value, + Err(err) => { + error!("Failed to convert OutgoingMessage to JSON value: {err}"); + return None; + } + }; + match serde_json::to_string(&value) { + Ok(json) => Some(json), + Err(err) => { + error!("Failed to serialize JSONRPCMessage: {err}"); + None + } + } +} + +pub(crate) async fn route_outgoing_envelope( + connections: &mut HashMap, + envelope: OutgoingEnvelope, +) { + match envelope { + OutgoingEnvelope::ToConnection { + connection_id, + message, + } => { + let Some(connection_state) = connections.get(&connection_id) else { + warn!( + "dropping message for disconnected connection: {:?}", + connection_id + ); + return; + }; + if connection_state.writer.send(message).await.is_err() { + connections.remove(&connection_id); + } + } + OutgoingEnvelope::Broadcast { message } => { + let target_connections: Vec = connections + .iter() + .filter_map(|(connection_id, connection_state)| { + if connection_state.session.initialized { + Some(*connection_id) + } else { + None + } + }) + .collect(); + + for connection_id in target_connections { + let Some(connection_state) = connections.get(&connection_id) else { + continue; + }; + if connection_state.writer.send(message.clone()).await.is_err() { + connections.remove(&connection_id); + } + } + } + } +} + +pub(crate) fn has_initialized_connections( + connections: &HashMap, +) -> bool { + connections + .values() + .any(|connection| connection.session.initialized) +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn app_server_transport_parses_stdio_listen_url() { + let transport = AppServerTransport::from_listen_url(AppServerTransport::DEFAULT_LISTEN_URL) + .expect("stdio listen URL should parse"); + assert_eq!(transport, AppServerTransport::Stdio); + } + + #[test] + fn app_server_transport_parses_websocket_listen_url() { + let transport = AppServerTransport::from_listen_url("ws://127.0.0.1:1234") + .expect("websocket listen URL should parse"); + assert_eq!( + transport, + AppServerTransport::WebSocket { + bind_address: "127.0.0.1:1234".parse().expect("valid socket address"), + } + ); + } + + #[test] + fn app_server_transport_rejects_invalid_websocket_listen_url() { + let err = AppServerTransport::from_listen_url("ws://localhost:1234") + .expect_err("hostname bind address should be rejected"); + assert_eq!( + err.to_string(), + "invalid websocket --listen URL `ws://localhost:1234`; expected `ws://IP:PORT`" + ); + } + + #[test] + fn app_server_transport_rejects_unsupported_listen_url() { + let err = AppServerTransport::from_listen_url("http://127.0.0.1:1234") + .expect_err("unsupported scheme should fail"); + assert_eq!( + err.to_string(), + "unsupported --listen URL `http://127.0.0.1:1234`; expected `stdio://` or `ws://IP:PORT`" + ); + } +} diff --git a/codex-rs/app-server/tests/common/mcp_process.rs b/codex-rs/app-server/tests/common/mcp_process.rs index 4804a8cd37..57c29fcf9f 100644 --- a/codex-rs/app-server/tests/common/mcp_process.rs +++ b/codex-rs/app-server/tests/common/mcp_process.rs @@ -22,6 +22,7 @@ use codex_app_server_protocol::CollaborationModeListParams; use codex_app_server_protocol::ConfigBatchWriteParams; use codex_app_server_protocol::ConfigReadParams; use codex_app_server_protocol::ConfigValueWriteParams; +use codex_app_server_protocol::ExperimentalFeatureListParams; use codex_app_server_protocol::FeedbackUploadParams; use codex_app_server_protocol::ForkConversationParams; use codex_app_server_protocol::GetAccountParams; @@ -50,6 +51,7 @@ use codex_app_server_protocol::SendUserTurnParams; use codex_app_server_protocol::ServerRequest; use codex_app_server_protocol::SetDefaultModelParams; use codex_app_server_protocol::ThreadArchiveParams; +use codex_app_server_protocol::ThreadCompactStartParams; use codex_app_server_protocol::ThreadForkParams; use codex_app_server_protocol::ThreadListParams; use codex_app_server_protocol::ThreadLoadedListParams; @@ -60,6 +62,7 @@ use codex_app_server_protocol::ThreadStartParams; use codex_app_server_protocol::ThreadUnarchiveParams; use codex_app_server_protocol::TurnInterruptParams; use codex_app_server_protocol::TurnStartParams; +use codex_app_server_protocol::TurnSteerParams; use codex_core::default_client::CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR; use tokio::process::Command; @@ -418,6 +421,15 @@ impl McpProcess { self.send_request("thread/unarchive", params).await } + /// Send a `thread/compact/start` JSON-RPC request. + pub async fn send_thread_compact_start_request( + &mut self, + params: ThreadCompactStartParams, + ) -> anyhow::Result { + let params = Some(serde_json::to_value(params)?); + self.send_request("thread/compact/start", params).await + } + /// Send a `thread/rollback` JSON-RPC request. pub async fn send_thread_rollback_request( &mut self, @@ -463,6 +475,15 @@ impl McpProcess { self.send_request("model/list", params).await } + /// Send an `experimentalFeature/list` JSON-RPC request. + pub async fn send_experimental_feature_list_request( + &mut self, + params: ExperimentalFeatureListParams, + ) -> anyhow::Result { + let params = Some(serde_json::to_value(params)?); + self.send_request("experimentalFeature/list", params).await + } + /// Send an `app/list` JSON-RPC request. pub async fn send_apps_list_request(&mut self, params: AppsListParams) -> anyhow::Result { let params = Some(serde_json::to_value(params)?); @@ -537,6 +558,15 @@ impl McpProcess { self.send_request("turn/interrupt", params).await } + /// Send a `turn/steer` JSON-RPC request (v2). + pub async fn send_turn_steer_request( + &mut self, + params: TurnSteerParams, + ) -> anyhow::Result { + let params = Some(serde_json::to_value(params)?); + self.send_request("turn/steer", params).await + } + /// Send a `review/start` JSON-RPC request (v2). pub async fn send_review_start_request( &mut self, diff --git a/codex-rs/app-server/tests/suite/codex_message_processor_flow.rs b/codex-rs/app-server/tests/suite/codex_message_processor_flow.rs index d905c3c1b5..2debbda653 100644 --- a/codex-rs/app-server/tests/suite/codex_message_processor_flow.rs +++ b/codex-rs/app-server/tests/suite/codex_message_processor_flow.rs @@ -36,7 +36,7 @@ use std::path::Path; use tempfile::TempDir; use tokio::time::timeout; -const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); +const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(20); #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn test_codex_jsonrpc_conversation_flow() -> Result<()> { @@ -76,6 +76,7 @@ async fn test_codex_jsonrpc_conversation_flow() -> Result<()> { let new_conv_id = mcp .send_new_conversation_request(NewConversationParams { cwd: Some(working_directory.to_string_lossy().into_owned()), + sandbox: Some(SandboxMode::DangerFullAccess), ..Default::default() }) .await?; @@ -528,6 +529,7 @@ fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::Result<() r#" model = "mock-model" approval_policy = "untrusted" +sandbox_mode = "danger-full-access" model_provider = "mock_provider" diff --git a/codex-rs/app-server/tests/suite/interrupt.rs b/codex-rs/app-server/tests/suite/interrupt.rs index f8dc2a7e8e..2270afce28 100644 --- a/codex-rs/app-server/tests/suite/interrupt.rs +++ b/codex-rs/app-server/tests/suite/interrupt.rs @@ -147,7 +147,7 @@ fn create_config_toml(codex_home: &Path, server_uri: String) -> std::io::Result< r#" model = "mock-model" approval_policy = "never" -sandbox_mode = "read-only" +sandbox_mode = "danger-full-access" model_provider = "mock_provider" diff --git a/codex-rs/app-server/tests/suite/send_message.rs b/codex-rs/app-server/tests/suite/send_message.rs index 814352a007..ecb742aff0 100644 --- a/codex-rs/app-server/tests/suite/send_message.rs +++ b/codex-rs/app-server/tests/suite/send_message.rs @@ -1,5 +1,7 @@ use anyhow::Result; use app_test_support::McpProcess; +use app_test_support::create_fake_rollout; +use app_test_support::rollout_path; use app_test_support::to_response; use codex_app_server_protocol::AddConversationListenerParams; use codex_app_server_protocol::AddConversationSubscriptionResponse; @@ -9,18 +11,25 @@ use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::NewConversationParams; use codex_app_server_protocol::NewConversationResponse; use codex_app_server_protocol::RequestId; +use codex_app_server_protocol::ResumeConversationParams; +use codex_app_server_protocol::ResumeConversationResponse; use codex_app_server_protocol::SendUserMessageParams; use codex_app_server_protocol::SendUserMessageResponse; use codex_execpolicy::Policy; use codex_protocol::ThreadId; +use codex_protocol::config_types::ReasoningSummary; use codex_protocol::models::ContentItem; use codex_protocol::models::DeveloperInstructions; use codex_protocol::models::ResponseItem; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::RawResponseItemEvent; +use codex_protocol::protocol::RolloutItem; +use codex_protocol::protocol::RolloutLine; use codex_protocol::protocol::SandboxPolicy; +use codex_protocol::protocol::TurnContextItem; use core_test_support::responses; use pretty_assertions::assert_eq; +use std::io::Write; use std::path::Path; use std::path::PathBuf; use tempfile::TempDir; @@ -263,6 +272,114 @@ async fn test_send_message_session_not_found() -> Result<()> { Ok(()) } +#[tokio::test] +async fn resume_with_model_mismatch_appends_model_switch_once() -> Result<()> { + let server = responses::start_mock_server().await; + let response_mock = responses::mount_sse_sequence( + &server, + vec![ + responses::sse(vec![ + responses::ev_response_created("resp-1"), + responses::ev_assistant_message("msg-1", "Done"), + responses::ev_completed("resp-1"), + ]), + responses::sse(vec![ + responses::ev_response_created("resp-2"), + responses::ev_assistant_message("msg-2", "Done again"), + responses::ev_completed("resp-2"), + ]), + ], + ) + .await; + + let codex_home = TempDir::new()?; + create_config_toml(codex_home.path(), &server.uri())?; + + let filename_ts = "2025-01-02T12-00-00"; + let meta_rfc3339 = "2025-01-02T12:00:00Z"; + let preview = "Resume me"; + let conversation_id = create_fake_rollout( + codex_home.path(), + filename_ts, + meta_rfc3339, + preview, + Some("mock_provider"), + None, + )?; + let rollout_path = rollout_path(codex_home.path(), filename_ts, &conversation_id); + append_rollout_turn_context(&rollout_path, meta_rfc3339, "previous-model")?; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let resume_id = mcp + .send_resume_conversation_request(ResumeConversationParams { + path: Some(rollout_path.clone()), + conversation_id: None, + history: None, + overrides: Some(NewConversationParams { + model: Some("gpt-5.2-codex".to_string()), + ..Default::default() + }), + }) + .await?; + timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_notification_message("sessionConfigured"), + ) + .await??; + let resume_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(resume_id)), + ) + .await??; + let ResumeConversationResponse { + conversation_id, .. + } = to_response::(resume_resp)?; + + let add_listener_id = mcp + .send_add_conversation_listener_request(AddConversationListenerParams { + conversation_id, + experimental_raw_events: false, + }) + .await?; + let add_listener_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(add_listener_id)), + ) + .await??; + let AddConversationSubscriptionResponse { subscription_id: _ } = + to_response::<_>(add_listener_resp)?; + + send_message("hello after resume", conversation_id, &mut mcp).await?; + send_message("second turn", conversation_id, &mut mcp).await?; + + let requests = response_mock.requests(); + assert_eq!(requests.len(), 2, "expected two model requests"); + + let first_developer_texts = requests[0].message_input_texts("developer"); + let first_model_switch_count = first_developer_texts + .iter() + .filter(|text| text.contains("")) + .count(); + assert!( + first_model_switch_count >= 1, + "expected model switch message on first post-resume turn, got {first_developer_texts:?}" + ); + + let second_developer_texts = requests[1].message_input_texts("developer"); + let second_model_switch_count = second_developer_texts + .iter() + .filter(|text| text.contains("")) + .count(); + assert_eq!( + second_model_switch_count, 1, + "did not expect duplicate model switch message on second post-resume turn, got {second_developer_texts:?}" + ); + + Ok(()) +} + // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- @@ -438,3 +555,28 @@ fn content_texts(content: &[ContentItem]) -> Vec<&str> { }) .collect() } + +fn append_rollout_turn_context(path: &Path, timestamp: &str, model: &str) -> std::io::Result<()> { + let line = RolloutLine { + timestamp: timestamp.to_string(), + item: RolloutItem::TurnContext(TurnContextItem { + cwd: PathBuf::from("/"), + approval_policy: AskForApproval::Never, + sandbox_policy: SandboxPolicy::DangerFullAccess, + model: model.to_string(), + personality: None, + collaboration_mode: None, + effort: None, + summary: ReasoningSummary::Auto, + user_instructions: None, + developer_instructions: None, + final_output_json_schema: None, + truncation_policy: None, + }), + }; + let serialized = serde_json::to_string(&line).map_err(std::io::Error::other)?; + std::fs::OpenOptions::new() + .append(true) + .open(path)? + .write_all(format!("{serialized}\n").as_bytes()) +} diff --git a/codex-rs/app-server/tests/suite/v2/compaction.rs b/codex-rs/app-server/tests/suite/v2/compaction.rs index eeedcb286b..4730d920c8 100644 --- a/codex-rs/app-server/tests/suite/v2/compaction.rs +++ b/codex-rs/app-server/tests/suite/v2/compaction.rs @@ -15,9 +15,12 @@ use app_test_support::write_chatgpt_auth; use app_test_support::write_mock_responses_config_toml; use codex_app_server_protocol::ItemCompletedNotification; use codex_app_server_protocol::ItemStartedNotification; +use codex_app_server_protocol::JSONRPCError; use codex_app_server_protocol::JSONRPCNotification; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::RequestId; +use codex_app_server_protocol::ThreadCompactStartParams; +use codex_app_server_protocol::ThreadCompactStartResponse; use codex_app_server_protocol::ThreadItem; use codex_app_server_protocol::ThreadStartParams; use codex_app_server_protocol::ThreadStartResponse; @@ -39,6 +42,7 @@ use tokio::time::timeout; const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); const AUTO_COMPACT_LIMIT: i64 = 1_000; const COMPACT_PROMPT: &str = "Summarize the conversation."; +const INVALID_REQUEST_ERROR_CODE: i64 = -32600; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn auto_compaction_local_emits_started_and_completed_items() -> Result<()> { @@ -196,6 +200,134 @@ async fn auto_compaction_remote_emits_started_and_completed_items() -> Result<() Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn thread_compact_start_triggers_compaction_and_returns_empty_response() -> Result<()> { + skip_if_no_network!(Ok(())); + + let server = responses::start_mock_server().await; + let sse = responses::sse(vec![ + responses::ev_assistant_message("m1", "MANUAL_COMPACT_SUMMARY"), + responses::ev_completed_with_tokens("r1", 200), + ]); + responses::mount_sse_sequence(&server, vec![sse]).await; + + let codex_home = TempDir::new()?; + write_mock_responses_config_toml( + codex_home.path(), + &server.uri(), + &BTreeMap::default(), + AUTO_COMPACT_LIMIT, + None, + "mock_provider", + COMPACT_PROMPT, + )?; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let thread_id = start_thread(&mut mcp).await?; + let compact_id = mcp + .send_thread_compact_start_request(ThreadCompactStartParams { + thread_id: thread_id.clone(), + }) + .await?; + let compact_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(compact_id)), + ) + .await??; + let _compact: ThreadCompactStartResponse = + to_response::(compact_resp)?; + + let started = wait_for_context_compaction_started(&mut mcp).await?; + let completed = wait_for_context_compaction_completed(&mut mcp).await?; + + let ThreadItem::ContextCompaction { id: started_id } = started.item else { + unreachable!("started item should be context compaction"); + }; + let ThreadItem::ContextCompaction { id: completed_id } = completed.item else { + unreachable!("completed item should be context compaction"); + }; + + assert_eq!(started.thread_id, thread_id); + assert_eq!(completed.thread_id, thread_id); + assert_eq!(started_id, completed_id); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn thread_compact_start_rejects_invalid_thread_id() -> Result<()> { + skip_if_no_network!(Ok(())); + + let server = responses::start_mock_server().await; + let codex_home = TempDir::new()?; + write_mock_responses_config_toml( + codex_home.path(), + &server.uri(), + &BTreeMap::default(), + AUTO_COMPACT_LIMIT, + None, + "mock_provider", + COMPACT_PROMPT, + )?; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let request_id = mcp + .send_thread_compact_start_request(ThreadCompactStartParams { + thread_id: "not-a-thread-id".to_string(), + }) + .await?; + let error: JSONRPCError = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_error_message(RequestId::Integer(request_id)), + ) + .await??; + + assert_eq!(error.error.code, INVALID_REQUEST_ERROR_CODE); + assert!(error.error.message.contains("invalid thread id")); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn thread_compact_start_rejects_unknown_thread_id() -> Result<()> { + skip_if_no_network!(Ok(())); + + let server = responses::start_mock_server().await; + let codex_home = TempDir::new()?; + write_mock_responses_config_toml( + codex_home.path(), + &server.uri(), + &BTreeMap::default(), + AUTO_COMPACT_LIMIT, + None, + "mock_provider", + COMPACT_PROMPT, + )?; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let request_id = mcp + .send_thread_compact_start_request(ThreadCompactStartParams { + thread_id: "67e55044-10b1-426f-9247-bb680e5fe0c8".to_string(), + }) + .await?; + let error: JSONRPCError = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_error_message(RequestId::Integer(request_id)), + ) + .await??; + + assert_eq!(error.error.code, INVALID_REQUEST_ERROR_CODE); + assert!(error.error.message.contains("thread not found")); + + Ok(()) +} + async fn start_thread(mcp: &mut McpProcess) -> Result { let thread_id = mcp .send_thread_start_request(ThreadStartParams { diff --git a/codex-rs/app-server/tests/suite/v2/connection_handling_websocket.rs b/codex-rs/app-server/tests/suite/v2/connection_handling_websocket.rs new file mode 100644 index 0000000000..ddd4326fc9 --- /dev/null +++ b/codex-rs/app-server/tests/suite/v2/connection_handling_websocket.rs @@ -0,0 +1,263 @@ +use anyhow::Context; +use anyhow::Result; +use anyhow::bail; +use app_test_support::create_mock_responses_server_sequence_unchecked; +use codex_app_server_protocol::ClientInfo; +use codex_app_server_protocol::InitializeParams; +use codex_app_server_protocol::JSONRPCError; +use codex_app_server_protocol::JSONRPCMessage; +use codex_app_server_protocol::JSONRPCRequest; +use codex_app_server_protocol::JSONRPCResponse; +use codex_app_server_protocol::RequestId; +use futures::SinkExt; +use futures::StreamExt; +use serde_json::json; +use std::net::SocketAddr; +use std::path::Path; +use std::process::Stdio; +use tempfile::TempDir; +use tokio::io::AsyncBufReadExt; +use tokio::process::Child; +use tokio::process::Command; +use tokio::time::Duration; +use tokio::time::Instant; +use tokio::time::sleep; +use tokio::time::timeout; +use tokio_tungstenite::MaybeTlsStream; +use tokio_tungstenite::WebSocketStream; +use tokio_tungstenite::connect_async; +use tokio_tungstenite::tungstenite::Message as WebSocketMessage; + +const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(5); + +type WsClient = WebSocketStream>; + +#[tokio::test] +async fn websocket_transport_routes_per_connection_handshake_and_responses() -> Result<()> { + let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; + let codex_home = TempDir::new()?; + create_config_toml(codex_home.path(), &server.uri(), "never")?; + + let bind_addr = reserve_local_addr()?; + let mut process = spawn_websocket_server(codex_home.path(), bind_addr).await?; + + let mut ws1 = connect_websocket(bind_addr).await?; + let mut ws2 = connect_websocket(bind_addr).await?; + + send_initialize_request(&mut ws1, 1, "ws_client_one").await?; + let first_init = read_response_for_id(&mut ws1, 1).await?; + assert_eq!(first_init.id, RequestId::Integer(1)); + + // Initialize responses are request-scoped and must not leak to other + // connections. + assert_no_message(&mut ws2, Duration::from_millis(250)).await?; + + send_config_read_request(&mut ws2, 2).await?; + let not_initialized = read_error_for_id(&mut ws2, 2).await?; + assert_eq!(not_initialized.error.message, "Not initialized"); + + send_initialize_request(&mut ws2, 3, "ws_client_two").await?; + let second_init = read_response_for_id(&mut ws2, 3).await?; + assert_eq!(second_init.id, RequestId::Integer(3)); + + // Same request-id on different connections must route independently. + send_config_read_request(&mut ws1, 77).await?; + send_config_read_request(&mut ws2, 77).await?; + let ws1_config = read_response_for_id(&mut ws1, 77).await?; + let ws2_config = read_response_for_id(&mut ws2, 77).await?; + + assert_eq!(ws1_config.id, RequestId::Integer(77)); + assert_eq!(ws2_config.id, RequestId::Integer(77)); + assert!(ws1_config.result.get("config").is_some()); + assert!(ws2_config.result.get("config").is_some()); + + process + .kill() + .await + .context("failed to stop websocket app-server process")?; + Ok(()) +} + +async fn spawn_websocket_server(codex_home: &Path, bind_addr: SocketAddr) -> Result { + let program = codex_utils_cargo_bin::cargo_bin("codex-app-server") + .context("should find app-server binary")?; + let mut cmd = Command::new(program); + cmd.arg("--listen") + .arg(format!("ws://{bind_addr}")) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::piped()) + .env("CODEX_HOME", codex_home) + .env("RUST_LOG", "debug"); + let mut process = cmd + .kill_on_drop(true) + .spawn() + .context("failed to spawn websocket app-server process")?; + + if let Some(stderr) = process.stderr.take() { + let mut stderr_reader = tokio::io::BufReader::new(stderr).lines(); + tokio::spawn(async move { + while let Ok(Some(line)) = stderr_reader.next_line().await { + eprintln!("[websocket app-server stderr] {line}"); + } + }); + } + + Ok(process) +} + +fn reserve_local_addr() -> Result { + let listener = std::net::TcpListener::bind("127.0.0.1:0")?; + let addr = listener.local_addr()?; + drop(listener); + Ok(addr) +} + +async fn connect_websocket(bind_addr: SocketAddr) -> Result { + let url = format!("ws://{bind_addr}"); + let deadline = Instant::now() + Duration::from_secs(10); + loop { + match connect_async(&url).await { + Ok((stream, _response)) => return Ok(stream), + Err(err) => { + if Instant::now() >= deadline { + bail!("failed to connect websocket to {url}: {err}"); + } + sleep(Duration::from_millis(50)).await; + } + } + } +} + +async fn send_initialize_request(stream: &mut WsClient, id: i64, client_name: &str) -> Result<()> { + let params = InitializeParams { + client_info: ClientInfo { + name: client_name.to_string(), + title: Some("WebSocket Test Client".to_string()), + version: "0.1.0".to_string(), + }, + capabilities: None, + }; + send_request( + stream, + "initialize", + id, + Some(serde_json::to_value(params)?), + ) + .await +} + +async fn send_config_read_request(stream: &mut WsClient, id: i64) -> Result<()> { + send_request( + stream, + "config/read", + id, + Some(json!({ "includeLayers": false })), + ) + .await +} + +async fn send_request( + stream: &mut WsClient, + method: &str, + id: i64, + params: Option, +) -> Result<()> { + let message = JSONRPCMessage::Request(JSONRPCRequest { + id: RequestId::Integer(id), + method: method.to_string(), + params, + }); + send_jsonrpc(stream, message).await +} + +async fn send_jsonrpc(stream: &mut WsClient, message: JSONRPCMessage) -> Result<()> { + let payload = serde_json::to_string(&message)?; + stream + .send(WebSocketMessage::Text(payload.into())) + .await + .context("failed to send websocket frame") +} + +async fn read_response_for_id(stream: &mut WsClient, id: i64) -> Result { + let target_id = RequestId::Integer(id); + loop { + let message = read_jsonrpc_message(stream).await?; + if let JSONRPCMessage::Response(response) = message + && response.id == target_id + { + return Ok(response); + } + } +} + +async fn read_error_for_id(stream: &mut WsClient, id: i64) -> Result { + let target_id = RequestId::Integer(id); + loop { + let message = read_jsonrpc_message(stream).await?; + if let JSONRPCMessage::Error(err) = message + && err.id == target_id + { + return Ok(err); + } + } +} + +async fn read_jsonrpc_message(stream: &mut WsClient) -> Result { + loop { + let frame = timeout(DEFAULT_READ_TIMEOUT, stream.next()) + .await + .context("timed out waiting for websocket frame")? + .context("websocket stream ended unexpectedly")? + .context("failed to read websocket frame")?; + + match frame { + WebSocketMessage::Text(text) => return Ok(serde_json::from_str(text.as_ref())?), + WebSocketMessage::Ping(payload) => { + stream.send(WebSocketMessage::Pong(payload)).await?; + } + WebSocketMessage::Pong(_) => {} + WebSocketMessage::Close(frame) => { + bail!("websocket closed unexpectedly: {frame:?}") + } + WebSocketMessage::Binary(_) => bail!("unexpected binary websocket frame"), + WebSocketMessage::Frame(_) => {} + } + } +} + +async fn assert_no_message(stream: &mut WsClient, wait_for: Duration) -> Result<()> { + match timeout(wait_for, stream.next()).await { + Ok(Some(Ok(frame))) => bail!("unexpected frame while waiting for silence: {frame:?}"), + Ok(Some(Err(err))) => bail!("unexpected websocket read error: {err}"), + Ok(None) => bail!("websocket closed unexpectedly while waiting for silence"), + Err(_) => Ok(()), + } +} + +fn create_config_toml( + codex_home: &Path, + server_uri: &str, + approval_policy: &str, +) -> std::io::Result<()> { + let config_toml = codex_home.join("config.toml"); + std::fs::write( + config_toml, + format!( + r#" +model = "mock-model" +approval_policy = "{approval_policy}" +sandbox_mode = "read-only" + +model_provider = "mock_provider" + +[model_providers.mock_provider] +name = "Mock provider for test" +base_url = "{server_uri}/v1" +wire_api = "responses" +request_max_retries = 0 +stream_max_retries = 0 +"# + ), + ) +} diff --git a/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs b/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs index dc985ac49f..f4cd85a61d 100644 --- a/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs +++ b/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs @@ -4,6 +4,7 @@ use app_test_support::McpProcess; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; +use codex_app_server_protocol::DynamicToolCallOutputContentItem; use codex_app_server_protocol::DynamicToolCallParams; use codex_app_server_protocol::DynamicToolCallResponse; use codex_app_server_protocol::DynamicToolSpec; @@ -15,6 +16,9 @@ use codex_app_server_protocol::ThreadStartResponse; use codex_app_server_protocol::TurnStartParams; use codex_app_server_protocol::TurnStartResponse; use codex_app_server_protocol::UserInput as V2UserInput; +use codex_protocol::models::FunctionCallOutputBody; +use codex_protocol::models::FunctionCallOutputContentItem; +use codex_protocol::models::FunctionCallOutputPayload; use core_test_support::responses; use pretty_assertions::assert_eq; use serde_json::Value; @@ -111,7 +115,7 @@ async fn thread_start_injects_dynamic_tools_into_model_requests() -> Result<()> /// Exercises the full dynamic tool call path (server request, client response, model output). #[tokio::test] -async fn dynamic_tool_call_round_trip_sends_output_to_model() -> Result<()> { +async fn dynamic_tool_call_round_trip_sends_text_content_items_to_model() -> Result<()> { let call_id = "dyn-call-1"; let tool_name = "demo_tool"; let tool_args = json!({ "city": "Paris" }); @@ -200,7 +204,9 @@ async fn dynamic_tool_call_round_trip_sends_output_to_model() -> Result<()> { // Respond to the tool call so the model receives a function_call_output. let response = DynamicToolCallResponse { - output: "dynamic-ok".to_string(), + content_items: vec![DynamicToolCallOutputContentItem::InputText { + text: "dynamic-ok".to_string(), + }], success: true, }; mcp.send_response(request_id, serde_json::to_value(response)?) @@ -213,11 +219,171 @@ async fn dynamic_tool_call_round_trip_sends_output_to_model() -> Result<()> { .await??; let bodies = responses_bodies(&server).await?; - let output = bodies + let payload = bodies .iter() - .find_map(|body| function_call_output_text(body, call_id)) + .find_map(|body| function_call_output_payload(body, call_id)) .context("expected function_call_output in follow-up request")?; - assert_eq!(output, "dynamic-ok"); + let expected_payload = FunctionCallOutputPayload::from_content_items(vec![ + FunctionCallOutputContentItem::InputText { + text: "dynamic-ok".to_string(), + }, + ]); + assert_eq!(payload, expected_payload); + + Ok(()) +} + +/// Ensures dynamic tool call responses can include structured content items. +#[tokio::test] +async fn dynamic_tool_call_round_trip_sends_content_items_to_model() -> Result<()> { + let call_id = "dyn-call-items-1"; + let tool_name = "demo_tool"; + let tool_args = json!({ "city": "Paris" }); + let tool_call_arguments = serde_json::to_string(&tool_args)?; + + let responses = vec![ + responses::sse(vec![ + responses::ev_response_created("resp-1"), + responses::ev_function_call(call_id, tool_name, &tool_call_arguments), + responses::ev_completed("resp-1"), + ]), + create_final_assistant_message_sse_response("Done")?, + ]; + let server = create_mock_responses_server_sequence_unchecked(responses).await; + + let codex_home = TempDir::new()?; + create_config_toml(codex_home.path(), &server.uri())?; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let dynamic_tool = DynamicToolSpec { + name: tool_name.to_string(), + description: "Demo dynamic tool".to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "city": { "type": "string" } + }, + "required": ["city"], + "additionalProperties": false, + }), + }; + + let thread_req = mcp + .send_thread_start_request(ThreadStartParams { + dynamic_tools: Some(vec![dynamic_tool]), + ..Default::default() + }) + .await?; + let thread_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(thread_req)), + ) + .await??; + let ThreadStartResponse { thread, .. } = to_response::(thread_resp)?; + + let turn_req = mcp + .send_turn_start_request(TurnStartParams { + thread_id: thread.id.clone(), + input: vec![V2UserInput::Text { + text: "Run the tool".to_string(), + text_elements: Vec::new(), + }], + ..Default::default() + }) + .await?; + let turn_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(turn_req)), + ) + .await??; + let TurnStartResponse { turn } = to_response::(turn_resp)?; + + let request = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_request_message(), + ) + .await??; + let (request_id, params) = match request { + ServerRequest::DynamicToolCall { request_id, params } => (request_id, params), + other => panic!("expected DynamicToolCall request, got {other:?}"), + }; + + let expected = DynamicToolCallParams { + thread_id: thread.id, + turn_id: turn.id, + call_id: call_id.to_string(), + tool: tool_name.to_string(), + arguments: tool_args, + }; + assert_eq!(params, expected); + + let response_content_items = vec![ + DynamicToolCallOutputContentItem::InputText { + text: "dynamic-ok".to_string(), + }, + DynamicToolCallOutputContentItem::InputImage { + image_url: "data:image/png;base64,AAA".to_string(), + }, + ]; + let content_items = response_content_items + .clone() + .into_iter() + .map(|item| match item { + DynamicToolCallOutputContentItem::InputText { text } => { + FunctionCallOutputContentItem::InputText { text } + } + DynamicToolCallOutputContentItem::InputImage { image_url } => { + FunctionCallOutputContentItem::InputImage { image_url } + } + }) + .collect::>(); + let response = DynamicToolCallResponse { + content_items: response_content_items, + success: true, + }; + mcp.send_response(request_id, serde_json::to_value(response)?) + .await?; + + timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_notification_message("turn/completed"), + ) + .await??; + + let bodies = responses_bodies(&server).await?; + let output_value = bodies + .iter() + .find_map(|body| function_call_output_raw_output(body, call_id)) + .context("expected function_call_output output in follow-up request")?; + assert_eq!( + output_value, + json!([ + { + "type": "input_text", + "text": "dynamic-ok" + }, + { + "type": "input_image", + "image_url": "data:image/png;base64,AAA" + } + ]) + ); + + let payload = bodies + .iter() + .find_map(|body| function_call_output_payload(body, call_id)) + .context("expected function_call_output in follow-up request")?; + assert_eq!( + payload.body, + FunctionCallOutputBody::ContentItems(content_items.clone()) + ); + assert_eq!(payload.success, None); + assert_eq!( + serde_json::to_string(&payload)?, + serde_json::to_string(&content_items)? + ); Ok(()) } @@ -248,7 +414,12 @@ fn find_tool<'a>(body: &'a Value, name: &str) -> Option<&'a Value> { }) } -fn function_call_output_text(body: &Value, call_id: &str) -> Option { +fn function_call_output_payload(body: &Value, call_id: &str) -> Option { + function_call_output_raw_output(body, call_id) + .and_then(|output| serde_json::from_value(output).ok()) +} + +fn function_call_output_raw_output(body: &Value, call_id: &str) -> Option { body.get("input") .and_then(Value::as_array) .and_then(|items| { @@ -258,8 +429,7 @@ fn function_call_output_text(body: &Value, call_id: &str) -> Option { }) }) .and_then(|item| item.get("output")) - .and_then(Value::as_str) - .map(str::to_string) + .cloned() } fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::Result<()> { diff --git a/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs b/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs new file mode 100644 index 0000000000..fdcbaca5b0 --- /dev/null +++ b/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs @@ -0,0 +1,78 @@ +use std::time::Duration; + +use anyhow::Result; +use app_test_support::McpProcess; +use app_test_support::to_response; +use codex_app_server_protocol::ExperimentalFeature; +use codex_app_server_protocol::ExperimentalFeatureListParams; +use codex_app_server_protocol::ExperimentalFeatureListResponse; +use codex_app_server_protocol::ExperimentalFeatureStage; +use codex_app_server_protocol::JSONRPCResponse; +use codex_app_server_protocol::RequestId; +use codex_core::features::FEATURES; +use codex_core::features::Stage; +use pretty_assertions::assert_eq; +use tempfile::TempDir; +use tokio::time::timeout; + +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); + +#[tokio::test] +async fn experimental_feature_list_returns_feature_metadata_with_stage() -> Result<()> { + let codex_home = TempDir::new()?; + let mut mcp = McpProcess::new(codex_home.path()).await?; + + timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; + + let request_id = mcp + .send_experimental_feature_list_request(ExperimentalFeatureListParams::default()) + .await?; + + let response: JSONRPCResponse = timeout( + DEFAULT_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(request_id)), + ) + .await??; + + let actual = to_response::(response)?; + let expected_data = FEATURES + .iter() + .map(|spec| { + let (stage, display_name, description, announcement) = match spec.stage { + Stage::Experimental { + name, + menu_description, + announcement, + } => ( + ExperimentalFeatureStage::Beta, + Some(name.to_string()), + Some(menu_description.to_string()), + Some(announcement.to_string()), + ), + Stage::UnderDevelopment => { + (ExperimentalFeatureStage::UnderDevelopment, None, None, None) + } + Stage::Stable => (ExperimentalFeatureStage::Stable, None, None, None), + Stage::Deprecated => (ExperimentalFeatureStage::Deprecated, None, None, None), + Stage::Removed => (ExperimentalFeatureStage::Removed, None, None, None), + }; + + ExperimentalFeature { + name: spec.key.to_string(), + stage, + display_name, + description, + announcement, + enabled: spec.default_enabled, + default_enabled: spec.default_enabled, + } + }) + .collect::>(); + let expected = ExperimentalFeatureListResponse { + data: expected_data, + next_cursor: None, + }; + + assert_eq!(actual, expected); + Ok(()) +} diff --git a/codex-rs/app-server/tests/suite/v2/mod.rs b/codex-rs/app-server/tests/suite/v2/mod.rs index 3a84e48b9e..c25a6d0568 100644 --- a/codex-rs/app-server/tests/suite/v2/mod.rs +++ b/codex-rs/app-server/tests/suite/v2/mod.rs @@ -4,8 +4,10 @@ mod app_list; mod collaboration_mode_list; mod compaction; mod config_rpc; +mod connection_handling_websocket; mod dynamic_tools; mod experimental_api; +mod experimental_feature_list; mod initialize; mod model_list; mod output_schema; @@ -24,3 +26,4 @@ mod thread_start; mod thread_unarchive; mod turn_interrupt; mod turn_start; +mod turn_steer; diff --git a/codex-rs/app-server/tests/suite/v2/model_list.rs b/codex-rs/app-server/tests/suite/v2/model_list.rs index c3b4ec7088..c2431d24d0 100644 --- a/codex-rs/app-server/tests/suite/v2/model_list.rs +++ b/codex-rs/app-server/tests/suite/v2/model_list.rs @@ -51,6 +51,7 @@ async fn list_models_returns_all_models_with_large_limit() -> Result<()> { Model { id: "gpt-5.2-codex".to_string(), model: "gpt-5.2-codex".to_string(), + upgrade: None, display_name: "gpt-5.2-codex".to_string(), description: "Latest frontier agentic coding model.".to_string(), supported_reasoning_efforts: vec![ @@ -80,6 +81,7 @@ async fn list_models_returns_all_models_with_large_limit() -> Result<()> { Model { id: "gpt-5.1-codex-max".to_string(), model: "gpt-5.1-codex-max".to_string(), + upgrade: Some("gpt-5.2-codex".to_string()), display_name: "gpt-5.1-codex-max".to_string(), description: "Codex-optimized flagship for deep and fast reasoning.".to_string(), supported_reasoning_efforts: vec![ @@ -109,6 +111,7 @@ async fn list_models_returns_all_models_with_large_limit() -> Result<()> { Model { id: "gpt-5.1-codex-mini".to_string(), model: "gpt-5.1-codex-mini".to_string(), + upgrade: Some("gpt-5.2-codex".to_string()), display_name: "gpt-5.1-codex-mini".to_string(), description: "Optimized for codex. Cheaper, faster, but less capable.".to_string(), supported_reasoning_efforts: vec![ @@ -130,6 +133,7 @@ async fn list_models_returns_all_models_with_large_limit() -> Result<()> { Model { id: "gpt-5.2".to_string(), model: "gpt-5.2".to_string(), + upgrade: Some("gpt-5.2-codex".to_string()), display_name: "gpt-5.2".to_string(), description: "Latest frontier model with improvements across knowledge, reasoning and coding" diff --git a/codex-rs/app-server/tests/suite/v2/thread_resume.rs b/codex-rs/app-server/tests/suite/v2/thread_resume.rs index 358fec351f..28b604001d 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_resume.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_resume.rs @@ -403,7 +403,7 @@ async fn thread_resume_accepts_personality_override() -> Result<()> { .send_thread_resume_request(ThreadResumeParams { thread_id: thread.id.clone(), model: Some("gpt-5.2-codex".to_string()), - personality: Some(Personality::Pragmatic), + personality: Some(Personality::Friendly), ..Default::default() }) .await?; diff --git a/codex-rs/app-server/tests/suite/v2/turn_interrupt.rs b/codex-rs/app-server/tests/suite/v2/turn_interrupt.rs index 9c804aa666..486e915f6f 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_interrupt.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_interrupt.rs @@ -129,7 +129,7 @@ fn create_config_toml(codex_home: &std::path::Path, server_uri: &str) -> std::io r#" model = "mock-model" approval_policy = "never" -sandbox_mode = "workspace-write" +sandbox_mode = "danger-full-access" model_provider = "mock_provider" diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 67a147c39e..395561f69a 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -2,6 +2,7 @@ use anyhow::Result; use app_test_support::McpProcess; use app_test_support::create_apply_patch_sse_response; use app_test_support::create_exec_command_sse_response; +use app_test_support::create_fake_rollout; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence; use app_test_support::create_mock_responses_server_sequence_unchecked; @@ -34,8 +35,10 @@ use codex_app_server_protocol::TurnStartResponse; use codex_app_server_protocol::TurnStartedNotification; use codex_app_server_protocol::TurnStatus; use codex_app_server_protocol::UserInput as V2UserInput; +use codex_core::config::ConfigToml; use codex_core::features::FEATURES; use codex_core::features::Feature; +use codex_core::personality_migration::PERSONALITY_MIGRATION_FILENAME; use codex_core::protocol_config_types::ReasoningSummary; use codex_protocol::config_types::CollaborationMode; use codex_protocol::config_types::ModeKind; @@ -52,6 +55,7 @@ use tokio::time::timeout; const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); const TEST_ORIGINATOR: &str = "codex_vscode"; +const LOCAL_PRAGMATIC_TEMPLATE: &str = "You are a deeply pragmatic, effective software engineer."; #[tokio::test] async fn turn_start_sends_originator_header() -> Result<()> { @@ -451,7 +455,7 @@ async fn turn_start_accepts_personality_override_v2() -> Result<()> { text: "Hello".to_string(), text_elements: Vec::new(), }], - personality: Some(Personality::Pragmatic), + personality: Some(Personality::Friendly), ..Default::default() }) .await?; @@ -556,7 +560,7 @@ async fn turn_start_change_personality_mid_thread_v2() -> Result<()> { text: "Hello again".to_string(), text_elements: Vec::new(), }], - personality: Some(Personality::Pragmatic), + personality: Some(Personality::Friendly), ..Default::default() }) .await?; @@ -595,6 +599,96 @@ async fn turn_start_change_personality_mid_thread_v2() -> Result<()> { Ok(()) } +#[tokio::test] +async fn turn_start_uses_migrated_pragmatic_personality_without_override_v2() -> Result<()> { + skip_if_no_network!(Ok(())); + + let server = responses::start_mock_server().await; + let body = responses::sse(vec![ + responses::ev_response_created("resp-1"), + responses::ev_assistant_message("msg-1", "Done"), + responses::ev_completed("resp-1"), + ]); + let response_mock = responses::mount_sse_once(&server, body).await; + + let codex_home = TempDir::new()?; + create_config_toml( + codex_home.path(), + &server.uri(), + "never", + &BTreeMap::from([(Feature::Personality, true)]), + )?; + create_fake_rollout( + codex_home.path(), + "2025-01-01T00-00-00", + "2025-01-01T00:00:00Z", + "history user message", + Some("mock_provider"), + None, + )?; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let persisted_toml: ConfigToml = toml::from_str(&std::fs::read_to_string( + codex_home.path().join("config.toml"), + )?)?; + assert_eq!(persisted_toml.personality, Some(Personality::Pragmatic)); + assert!( + codex_home + .path() + .join(PERSONALITY_MIGRATION_FILENAME) + .exists(), + "expected personality migration marker to be written on startup" + ); + + let thread_req = mcp + .send_thread_start_request(ThreadStartParams { + model: Some("gpt-5.2-codex".to_string()), + ..Default::default() + }) + .await?; + let thread_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(thread_req)), + ) + .await??; + let ThreadStartResponse { thread, .. } = to_response::(thread_resp)?; + + let turn_req = mcp + .send_turn_start_request(TurnStartParams { + thread_id: thread.id, + input: vec![V2UserInput::Text { + text: "Hello".to_string(), + text_elements: Vec::new(), + }], + personality: None, + ..Default::default() + }) + .await?; + let turn_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(turn_req)), + ) + .await??; + let _turn: TurnStartResponse = to_response::(turn_resp)?; + + timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_notification_message("turn/completed"), + ) + .await??; + + let request = response_mock.single_request(); + let instructions_text = request.instructions_text(); + assert!( + instructions_text.contains(LOCAL_PRAGMATIC_TEMPLATE), + "expected startup-migrated pragmatic personality in model instructions, got: {instructions_text:?}" + ); + + Ok(()) +} + #[tokio::test] async fn turn_start_accepts_local_image_input() -> Result<()> { // Two Codex turns hit the mock model (session start + turn/start). @@ -1622,11 +1716,12 @@ async fn command_execution_notifications_include_process_id() -> Result<()> { ]; let server = create_mock_responses_server_sequence(responses).await; let codex_home = TempDir::new()?; - create_config_toml( + create_config_toml_with_sandbox( codex_home.path(), &server.uri(), "never", &BTreeMap::from([(Feature::UnifiedExec, true)]), + "danger-full-access", )?; let mut mcp = McpProcess::new(codex_home.path()).await?; @@ -1652,6 +1747,7 @@ async fn command_execution_notifications_include_process_id() -> Result<()> { text: "run a command".to_string(), text_elements: Vec::new(), }], + sandbox_policy: Some(codex_app_server_protocol::SandboxPolicy::DangerFullAccess), ..Default::default() }) .await?; @@ -1752,6 +1848,22 @@ fn create_config_toml( server_uri: &str, approval_policy: &str, feature_flags: &BTreeMap, +) -> std::io::Result<()> { + create_config_toml_with_sandbox( + codex_home, + server_uri, + approval_policy, + feature_flags, + "read-only", + ) +} + +fn create_config_toml_with_sandbox( + codex_home: &Path, + server_uri: &str, + approval_policy: &str, + feature_flags: &BTreeMap, + sandbox_mode: &str, ) -> std::io::Result<()> { let mut features = BTreeMap::from([(Feature::RemoteModels, false)]); for (feature, enabled) in feature_flags { @@ -1776,7 +1888,7 @@ fn create_config_toml( r#" model = "mock-model" approval_policy = "{approval_policy}" -sandbox_mode = "read-only" +sandbox_mode = "{sandbox_mode}" model_provider = "mock_provider" diff --git a/codex-rs/app-server/tests/suite/v2/turn_steer.rs b/codex-rs/app-server/tests/suite/v2/turn_steer.rs new file mode 100644 index 0000000000..89704326fd --- /dev/null +++ b/codex-rs/app-server/tests/suite/v2/turn_steer.rs @@ -0,0 +1,179 @@ +#![cfg(unix)] + +use anyhow::Result; +use app_test_support::McpProcess; +use app_test_support::create_mock_responses_server_sequence; +use app_test_support::create_mock_responses_server_sequence_unchecked; +use app_test_support::create_shell_command_sse_response; +use app_test_support::to_response; +use codex_app_server_protocol::JSONRPCError; +use codex_app_server_protocol::JSONRPCNotification; +use codex_app_server_protocol::JSONRPCResponse; +use codex_app_server_protocol::RequestId; +use codex_app_server_protocol::ThreadStartParams; +use codex_app_server_protocol::ThreadStartResponse; +use codex_app_server_protocol::TurnStartParams; +use codex_app_server_protocol::TurnStartResponse; +use codex_app_server_protocol::TurnSteerParams; +use codex_app_server_protocol::TurnSteerResponse; +use codex_app_server_protocol::UserInput as V2UserInput; +use tempfile::TempDir; +use tokio::time::timeout; + +const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); + +#[tokio::test] +async fn turn_steer_requires_active_turn() -> Result<()> { + let tmp = TempDir::new()?; + let codex_home = tmp.path().join("codex_home"); + std::fs::create_dir(&codex_home)?; + + let server = create_mock_responses_server_sequence(vec![]).await; + create_config_toml(&codex_home, &server.uri())?; + + let mut mcp = McpProcess::new(&codex_home).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let thread_req = mcp + .send_thread_start_request(ThreadStartParams { + model: Some("mock-model".to_string()), + ..Default::default() + }) + .await?; + let thread_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(thread_req)), + ) + .await??; + let ThreadStartResponse { thread, .. } = to_response::(thread_resp)?; + + let steer_req = mcp + .send_turn_steer_request(TurnSteerParams { + thread_id: thread.id, + input: vec![V2UserInput::Text { + text: "steer".to_string(), + text_elements: Vec::new(), + }], + expected_turn_id: "turn-does-not-exist".to_string(), + }) + .await?; + let steer_err: JSONRPCError = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_error_message(RequestId::Integer(steer_req)), + ) + .await??; + assert_eq!(steer_err.error.code, -32600); + + Ok(()) +} + +#[tokio::test] +async fn turn_steer_returns_active_turn_id() -> Result<()> { + #[cfg(target_os = "windows")] + let shell_command = vec![ + "powershell".to_string(), + "-Command".to_string(), + "Start-Sleep -Seconds 10".to_string(), + ]; + #[cfg(not(target_os = "windows"))] + let shell_command = vec!["sleep".to_string(), "10".to_string()]; + + let tmp = TempDir::new()?; + let codex_home = tmp.path().join("codex_home"); + std::fs::create_dir(&codex_home)?; + let working_directory = tmp.path().join("workdir"); + std::fs::create_dir(&working_directory)?; + + let server = + create_mock_responses_server_sequence_unchecked(vec![create_shell_command_sse_response( + shell_command.clone(), + Some(&working_directory), + Some(10_000), + "call_sleep", + )?]) + .await; + create_config_toml(&codex_home, &server.uri())?; + + let mut mcp = McpProcess::new(&codex_home).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let thread_req = mcp + .send_thread_start_request(ThreadStartParams { + model: Some("mock-model".to_string()), + ..Default::default() + }) + .await?; + let thread_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(thread_req)), + ) + .await??; + let ThreadStartResponse { thread, .. } = to_response::(thread_resp)?; + + let turn_req = mcp + .send_turn_start_request(TurnStartParams { + thread_id: thread.id.clone(), + input: vec![V2UserInput::Text { + text: "run sleep".to_string(), + text_elements: Vec::new(), + }], + cwd: Some(working_directory.clone()), + ..Default::default() + }) + .await?; + let turn_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(turn_req)), + ) + .await??; + let TurnStartResponse { turn } = to_response::(turn_resp)?; + + let _task_started: JSONRPCNotification = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_notification_message("codex/event/task_started"), + ) + .await??; + + let steer_req = mcp + .send_turn_steer_request(TurnSteerParams { + thread_id: thread.id, + input: vec![V2UserInput::Text { + text: "steer".to_string(), + text_elements: Vec::new(), + }], + expected_turn_id: turn.id.clone(), + }) + .await?; + let steer_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(steer_req)), + ) + .await??; + let steer: TurnSteerResponse = to_response::(steer_resp)?; + assert_eq!(steer.turn_id, turn.id); + + Ok(()) +} + +fn create_config_toml(codex_home: &std::path::Path, server_uri: &str) -> std::io::Result<()> { + let config_toml = codex_home.join("config.toml"); + std::fs::write( + config_toml, + format!( + r#" +model = "mock-model" +approval_policy = "never" +sandbox_mode = "danger-full-access" + +model_provider = "mock_provider" + +[model_providers.mock_provider] +name = "Mock provider for test" +base_url = "{server_uri}/v1" +wire_api = "responses" +request_max_retries = 0 +stream_max_retries = 0 +"# + ), + ) +} diff --git a/codex-rs/cli/Cargo.toml b/codex-rs/cli/Cargo.toml index cfcfce88f9..77344df477 100644 --- a/codex-rs/cli/Cargo.toml +++ b/codex-rs/cli/Cargo.toml @@ -21,6 +21,7 @@ clap = { workspace = true, features = ["derive"] } clap_complete = { workspace = true } codex-app-server = { workspace = true } codex-app-server-protocol = { workspace = true } +codex-app-server-test-client = { workspace = true } codex-arg0 = { workspace = true } codex-chatgpt = { workspace = true } codex-cloud-tasks = { path = "../cloud-tasks" } diff --git a/codex-rs/cli/src/debug_sandbox.rs b/codex-rs/cli/src/debug_sandbox.rs index 5b165f9778..30352cbb83 100644 --- a/codex-rs/cli/src/debug_sandbox.rs +++ b/codex-rs/cli/src/debug_sandbox.rs @@ -227,16 +227,19 @@ async fn run_command_under_sandbox( .await? } SandboxType::Landlock => { + use codex_core::features::Feature; #[expect(clippy::expect_used)] let codex_linux_sandbox_exe = config .codex_linux_sandbox_exe .expect("codex-linux-sandbox executable not found"); + let use_bwrap_sandbox = config.features.enabled(Feature::UseLinuxSandboxBwrap); spawn_command_under_linux_sandbox( codex_linux_sandbox_exe, command, cwd, config.sandbox_policy.get(), sandbox_policy_cwd.as_path(), + use_bwrap_sandbox, stdio_policy, env, ) diff --git a/codex-rs/cli/src/login.rs b/codex-rs/cli/src/login.rs index 01a830acb2..8cb6f3d01f 100644 --- a/codex-rs/cli/src/login.rs +++ b/codex-rs/cli/src/login.rs @@ -1,7 +1,7 @@ -use codex_app_server_protocol::AuthMode; use codex_common::CliConfigOverrides; use codex_core::CodexAuth; use codex_core::auth::AuthCredentialsStoreMode; +use codex_core::auth::AuthMode; use codex_core::auth::CLIENT_ID; use codex_core::auth::login_with_api_key; use codex_core::auth::logout; @@ -225,7 +225,7 @@ pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! { let config = load_config_or_exit(cli_config_overrides).await; match CodexAuth::from_auth_storage(&config.codex_home, config.cli_auth_credentials_store_mode) { - Ok(Some(auth)) => match auth.api_auth_mode() { + Ok(Some(auth)) => match auth.auth_mode() { AuthMode::ApiKey => match auth.get_token() { Ok(api_key) => { eprintln!("Logged in using an API key - {}", safe_format_key(&api_key)); @@ -240,10 +240,6 @@ pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! { eprintln!("Logged in using ChatGPT"); std::process::exit(0); } - AuthMode::ChatgptAuthTokens => { - eprintln!("Logged in using ChatGPT (external tokens)"); - std::process::exit(0); - } }, Ok(None) => { eprintln!("Not logged in"); diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 022d6bbdf5..1cc4fcdaa7 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -110,9 +110,11 @@ enum Subcommand { Completion(CompletionCommand), /// Run commands within a Codex-provided sandbox. - #[clap(visible_alias = "debug")] Sandbox(SandboxArgs), + /// Debugging tools. + Debug(DebugCommand), + /// Execpolicy tooling. #[clap(hide = true)] Execpolicy(ExecpolicyCommand), @@ -150,6 +152,36 @@ struct CompletionCommand { shell: Shell, } +#[derive(Debug, Parser)] +struct DebugCommand { + #[command(subcommand)] + subcommand: DebugSubcommand, +} + +#[derive(Debug, clap::Subcommand)] +enum DebugSubcommand { + /// Tooling: helps debug the app server. + AppServer(DebugAppServerCommand), +} + +#[derive(Debug, Parser)] +struct DebugAppServerCommand { + #[command(subcommand)] + subcommand: DebugAppServerSubcommand, +} + +#[derive(Debug, clap::Subcommand)] +enum DebugAppServerSubcommand { + // Send message to app server V2. + SendMessageV2(DebugAppServerSendMessageV2Command), +} + +#[derive(Debug, Parser)] +struct DebugAppServerSendMessageV2Command { + #[arg(value_name = "USER_MESSAGE", required = true)] + user_message: String, +} + #[derive(Debug, Parser)] struct ResumeCommand { /// Conversation/session id (UUID) or thread name. UUIDs take precedence if it parses. @@ -274,6 +306,15 @@ struct AppServerCommand { #[command(subcommand)] subcommand: Option, + /// Transport endpoint URL. Supported values: `stdio://` (default), + /// `ws://IP:PORT`. + #[arg( + long = "listen", + value_name = "URL", + default_value = codex_app_server::AppServerTransport::DEFAULT_LISTEN_URL + )] + listen: codex_app_server::AppServerTransport, + /// Controls whether analytics are enabled by default. /// /// Analytics are disabled by default for app-server. Users have to explicitly opt in @@ -425,6 +466,15 @@ fn run_execpolicycheck(cmd: ExecPolicyCheckCommand) -> anyhow::Result<()> { cmd.run() } +fn run_debug_app_server_command(cmd: DebugAppServerCommand) -> anyhow::Result<()> { + match cmd.subcommand { + DebugAppServerSubcommand::SendMessageV2(cmd) => { + let codex_bin = std::env::current_exe()?; + codex_app_server_test_client::send_message_v2(&codex_bin, &[], cmd.user_message, &None) + } + } +} + #[derive(Debug, Default, Parser, Clone)] struct FeatureToggles { /// Enable a feature (repeatable). Equivalent to `-c features.=true`. @@ -546,11 +596,13 @@ async fn cli_main(codex_linux_sandbox_exe: Option) -> anyhow::Result<() } Some(Subcommand::AppServer(app_server_cli)) => match app_server_cli.subcommand { None => { - codex_app_server::run_main( + let transport = app_server_cli.listen; + codex_app_server::run_main_with_transport( codex_linux_sandbox_exe, root_config_overrides, codex_core::config_loader::LoaderOverrides::default(), app_server_cli.analytics_default_enabled, + transport, ) .await?; } @@ -693,6 +745,11 @@ async fn cli_main(codex_linux_sandbox_exe: Option) -> anyhow::Result<() .await?; } }, + Some(Subcommand::Debug(DebugCommand { subcommand })) => match subcommand { + DebugSubcommand::AppServer(cmd) => { + run_debug_app_server_command(cmd)?; + } + }, Some(Subcommand::Execpolicy(ExecpolicyCommand { sub })) => match sub { ExecpolicySubcommand::Check(cmd) => run_execpolicycheck(cmd)?, }, @@ -1282,6 +1339,10 @@ mod tests { fn app_server_analytics_default_disabled_without_flag() { let app_server = app_server_from_args(["codex", "app-server"].as_ref()); assert!(!app_server.analytics_default_enabled); + assert_eq!( + app_server.listen, + codex_app_server::AppServerTransport::Stdio + ); } #[test] @@ -1291,6 +1352,36 @@ mod tests { assert!(app_server.analytics_default_enabled); } + #[test] + fn app_server_listen_websocket_url_parses() { + let app_server = app_server_from_args( + ["codex", "app-server", "--listen", "ws://127.0.0.1:4500"].as_ref(), + ); + assert_eq!( + app_server.listen, + codex_app_server::AppServerTransport::WebSocket { + bind_address: "127.0.0.1:4500".parse().expect("valid socket address"), + } + ); + } + + #[test] + fn app_server_listen_stdio_url_parses() { + let app_server = + app_server_from_args(["codex", "app-server", "--listen", "stdio://"].as_ref()); + assert_eq!( + app_server.listen, + codex_app_server::AppServerTransport::Stdio + ); + } + + #[test] + fn app_server_listen_invalid_url_fails_to_parse() { + let parse_result = + MultitoolCli::try_parse_from(["codex", "app-server", "--listen", "http://foo"]); + assert!(parse_result.is_err()); + } + #[test] fn features_enable_parses_feature_name() { let cli = MultitoolCli::try_parse_from(["codex", "features", "enable", "unified_exec"]) diff --git a/codex-rs/cloud-requirements/src/lib.rs b/codex-rs/cloud-requirements/src/lib.rs index 535fef3876..9ca432dc7f 100644 --- a/codex-rs/cloud-requirements/src/lib.rs +++ b/codex-rs/cloud-requirements/src/lib.rs @@ -14,22 +14,33 @@ use codex_core::AuthManager; use codex_core::auth::CodexAuth; use codex_core::config_loader::CloudRequirementsLoader; use codex_core::config_loader::ConfigRequirementsToml; +use codex_core::util::backoff; use codex_protocol::account::PlanType; use std::sync::Arc; use std::time::Duration; use std::time::Instant; +use tokio::time::sleep; use tokio::time::timeout; -/// This blocks codex startup, so must be short. -const CLOUD_REQUIREMENTS_TIMEOUT: Duration = Duration::from_secs(5); +const CLOUD_REQUIREMENTS_TIMEOUT: Duration = Duration::from_secs(15); +const CLOUD_REQUIREMENTS_MAX_ATTEMPTS: usize = 5; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum FetchCloudRequirementsStatus { + BackendClientInit, + Request, + Parse, +} #[async_trait] trait RequirementsFetcher: Send + Sync { - /// Returns requirements as a TOML string. + /// Returns `Ok(None)` when there are no cloud requirements for the account. /// - /// TODO(gt): For now, returns an Option. But when we want to make this fail-closed, return a - /// Result. - async fn fetch_requirements(&self, auth: &CodexAuth) -> Option; + /// Returning `Err` indicates cloud requirements could not be fetched. + async fn fetch_requirements( + &self, + auth: &CodexAuth, + ) -> Result, FetchCloudRequirementsStatus>; } struct BackendRequirementsFetcher { @@ -44,7 +55,10 @@ impl BackendRequirementsFetcher { #[async_trait] impl RequirementsFetcher for BackendRequirementsFetcher { - async fn fetch_requirements(&self, auth: &CodexAuth) -> Option { + async fn fetch_requirements( + &self, + auth: &CodexAuth, + ) -> Result, FetchCloudRequirementsStatus> { let client = BackendClient::from_auth(self.base_url.clone(), auth) .inspect_err(|err| { tracing::warn!( @@ -52,20 +66,22 @@ impl RequirementsFetcher for BackendRequirementsFetcher { "Failed to construct backend client for cloud requirements" ); }) - .ok()?; + .map_err(|_| FetchCloudRequirementsStatus::BackendClientInit)?; let response = client .get_config_requirements_file() .await .inspect_err(|err| tracing::warn!(error = %err, "Failed to fetch cloud requirements")) - .ok()?; + .map_err(|_| FetchCloudRequirementsStatus::Request)?; let Some(contents) = response.contents else { - tracing::warn!("Cloud requirements response missing contents"); - return None; + tracing::info!( + "Cloud requirements response missing contents; treating as no requirements" + ); + return Ok(None); }; - Some(contents) + Ok(Some(contents)) } } @@ -129,11 +145,41 @@ impl CloudRequirementsService { return None; } - let contents = self.fetcher.fetch_requirements(&auth).await?; - parse_cloud_requirements(&contents) - .inspect_err(|err| tracing::warn!(error = %err, "Failed to parse cloud requirements")) - .ok() - .flatten() + self.fetch_with_retries(&auth).await + } + + async fn fetch_with_retries(&self, auth: &CodexAuth) -> Option { + for attempt in 1..=CLOUD_REQUIREMENTS_MAX_ATTEMPTS { + let fetch_result = self + .fetcher + .fetch_requirements(auth) + .await + .and_then(|contents| { + contents.map_or(Ok(None), |contents| { + parse_cloud_requirements(&contents).map_err(|err| { + tracing::warn!(error = %err, "Failed to parse cloud requirements"); + FetchCloudRequirementsStatus::Parse + }) + }) + }); + + match fetch_result { + Ok(requirements) => return requirements, + Err(status) => { + if attempt < CLOUD_REQUIREMENTS_MAX_ATTEMPTS { + tracing::warn!( + status = ?status, + attempt, + max_attempts = CLOUD_REQUIREMENTS_MAX_ATTEMPTS, + "Failed to fetch cloud requirements; retrying" + ); + sleep(backoff(attempt as u64)).await; + } + } + } + } + + None } } @@ -179,8 +225,11 @@ mod tests { use codex_protocol::protocol::AskForApproval; use pretty_assertions::assert_eq; use serde_json::json; + use std::collections::VecDeque; use std::future::pending; use std::path::Path; + use std::sync::atomic::AtomicUsize; + use std::sync::atomic::Ordering; use tempfile::tempdir; fn write_auth_json(codex_home: &Path, value: serde_json::Value) -> std::io::Result<()> { @@ -247,8 +296,11 @@ mod tests { #[async_trait::async_trait] impl RequirementsFetcher for StaticFetcher { - async fn fetch_requirements(&self, _auth: &CodexAuth) -> Option { - self.contents.clone() + async fn fetch_requirements( + &self, + _auth: &CodexAuth, + ) -> Result, FetchCloudRequirementsStatus> { + Ok(self.contents.clone()) } } @@ -256,9 +308,39 @@ mod tests { #[async_trait::async_trait] impl RequirementsFetcher for PendingFetcher { - async fn fetch_requirements(&self, _auth: &CodexAuth) -> Option { + async fn fetch_requirements( + &self, + _auth: &CodexAuth, + ) -> Result, FetchCloudRequirementsStatus> { pending::<()>().await; - None + Ok(None) + } + } + + struct SequenceFetcher { + responses: + tokio::sync::Mutex, FetchCloudRequirementsStatus>>>, + request_count: AtomicUsize, + } + + impl SequenceFetcher { + fn new(responses: Vec, FetchCloudRequirementsStatus>>) -> Self { + Self { + responses: tokio::sync::Mutex::new(VecDeque::from(responses)), + request_count: AtomicUsize::new(0), + } + } + } + + #[async_trait::async_trait] + impl RequirementsFetcher for SequenceFetcher { + async fn fetch_requirements( + &self, + _auth: &CodexAuth, + ) -> Result, FetchCloudRequirementsStatus> { + self.request_count.fetch_add(1, Ordering::SeqCst); + let mut responses = self.responses.lock().await; + responses.pop_front().unwrap_or(Ok(None)) } } @@ -360,4 +442,75 @@ mod tests { let result = handle.await.expect("cloud requirements task"); assert!(result.is_none()); } + + #[tokio::test(start_paused = true)] + async fn fetch_cloud_requirements_retries_until_success() { + let fetcher = Arc::new(SequenceFetcher::new(vec![ + Err(FetchCloudRequirementsStatus::Request), + Ok(Some("allowed_approval_policies = [\"never\"]".to_string())), + ])); + let service = CloudRequirementsService::new( + auth_manager_with_plan("business"), + fetcher.clone(), + CLOUD_REQUIREMENTS_TIMEOUT, + ); + + let handle = tokio::spawn(async move { service.fetch().await }); + tokio::task::yield_now().await; + tokio::time::advance(Duration::from_secs(1)).await; + + assert_eq!( + handle.await.expect("cloud requirements task"), + Some(ConfigRequirementsToml { + allowed_approval_policies: Some(vec![AskForApproval::Never]), + allowed_sandbox_modes: None, + mcp_servers: None, + rules: None, + enforce_residency: None, + }) + ); + assert_eq!(fetcher.request_count.load(Ordering::SeqCst), 2); + } + + #[tokio::test] + async fn fetch_cloud_requirements_none_is_success_without_retry() { + let fetcher = Arc::new(SequenceFetcher::new(vec![ + Ok(None), + Err(FetchCloudRequirementsStatus::Request), + ])); + let service = CloudRequirementsService::new( + auth_manager_with_plan("enterprise"), + fetcher.clone(), + CLOUD_REQUIREMENTS_TIMEOUT, + ); + + assert!(service.fetch().await.is_none()); + assert_eq!(fetcher.request_count.load(Ordering::SeqCst), 1); + } + + #[tokio::test(start_paused = true)] + async fn fetch_cloud_requirements_stops_after_max_retries() { + let fetcher = Arc::new(SequenceFetcher::new(vec![ + Err( + FetchCloudRequirementsStatus::Request + ); + CLOUD_REQUIREMENTS_MAX_ATTEMPTS + ])); + let service = CloudRequirementsService::new( + auth_manager_with_plan("enterprise"), + fetcher.clone(), + CLOUD_REQUIREMENTS_TIMEOUT, + ); + + let handle = tokio::spawn(async move { service.fetch().await }); + tokio::task::yield_now().await; + tokio::time::advance(Duration::from_secs(5)).await; + tokio::task::yield_now().await; + + assert!(handle.await.expect("cloud requirements task").is_none()); + assert_eq!( + fetcher.request_count.load(Ordering::SeqCst), + CLOUD_REQUIREMENTS_MAX_ATTEMPTS + ); + } } diff --git a/codex-rs/codex-api/README.md b/codex-rs/codex-api/README.md index c1f7d230c0..0570cf7570 100644 --- a/codex-rs/codex-api/README.md +++ b/codex-rs/codex-api/README.md @@ -29,4 +29,13 @@ The public interface of this crate is intentionally small and uniform: - Output: `Vec`. - `CompactClient::compact_input(&CompactionInput, extra_headers)` wraps the JSON encoding and retry/telemetry wiring. +- **Memory trace summarize endpoint** + - Input: `MemoryTraceSummarizeInput` (re-exported as `codex_api::MemoryTraceSummarizeInput`): + - `model: String`. + - `traces: Vec`. + - `MemoryTrace` includes `id`, `metadata.source_path`, and normalized `items`. + - `reasoning: Option`. + - Output: `Vec`. + - `MemoriesClient::trace_summarize_input(&MemoryTraceSummarizeInput, extra_headers)` wraps JSON encoding and retry/telemetry wiring. + All HTTP details (URLs, headers, retry/backoff policies, SSE framing) are encapsulated in `codex-api` and `codex-client`. Callers construct prompts/inputs using protocol types and work with typed streams of `ResponseEvent` or compacted `ResponseItem` values. diff --git a/codex-rs/codex-api/src/common.rs b/codex-rs/codex-api/src/common.rs index a9127644f1..f1a996f12c 100644 --- a/codex-rs/codex-api/src/common.rs +++ b/codex-rs/codex-api/src/common.rs @@ -6,6 +6,7 @@ use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; use codex_protocol::protocol::RateLimitSnapshot; use codex_protocol::protocol::TokenUsage; use futures::Stream; +use serde::Deserialize; use serde::Serialize; use serde_json::Value; use std::pin::Pin; @@ -37,6 +38,33 @@ pub struct CompactionInput<'a> { pub instructions: &'a str, } +/// Canonical input payload for the memory trace summarize endpoint. +#[derive(Debug, Clone, Serialize)] +pub struct MemoryTraceSummarizeInput { + pub model: String, + pub traces: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub reasoning: Option, +} + +#[derive(Debug, Clone, Serialize)] +pub struct MemoryTrace { + pub id: String, + pub metadata: MemoryTraceMetadata, + pub items: Vec, +} + +#[derive(Debug, Clone, Serialize)] +pub struct MemoryTraceMetadata { + pub source_path: String, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Eq)] +pub struct MemoryTraceSummaryOutput { + pub trace_summary: String, + pub memory_summary: String, +} + #[derive(Debug)] pub enum ResponseEvent { Created, diff --git a/codex-rs/codex-api/src/endpoint/memories.rs b/codex-rs/codex-api/src/endpoint/memories.rs new file mode 100644 index 0000000000..c8f35d7e16 --- /dev/null +++ b/codex-rs/codex-api/src/endpoint/memories.rs @@ -0,0 +1,108 @@ +use crate::auth::AuthProvider; +use crate::common::MemoryTraceSummarizeInput; +use crate::common::MemoryTraceSummaryOutput; +use crate::endpoint::session::EndpointSession; +use crate::error::ApiError; +use crate::provider::Provider; +use codex_client::HttpTransport; +use codex_client::RequestTelemetry; +use http::HeaderMap; +use http::Method; +use serde::Deserialize; +use serde_json::to_value; +use std::sync::Arc; + +pub struct MemoriesClient { + session: EndpointSession, +} + +impl MemoriesClient { + pub fn new(transport: T, provider: Provider, auth: A) -> Self { + Self { + session: EndpointSession::new(transport, provider, auth), + } + } + + pub fn with_telemetry(self, request: Option>) -> Self { + Self { + session: self.session.with_request_telemetry(request), + } + } + + fn path() -> &'static str { + "memories/trace_summarize" + } + + pub async fn trace_summarize( + &self, + body: serde_json::Value, + extra_headers: HeaderMap, + ) -> Result, ApiError> { + let resp = self + .session + .execute(Method::POST, Self::path(), extra_headers, Some(body)) + .await?; + let parsed: TraceSummarizeResponse = + serde_json::from_slice(&resp.body).map_err(|e| ApiError::Stream(e.to_string()))?; + Ok(parsed.output) + } + + pub async fn trace_summarize_input( + &self, + input: &MemoryTraceSummarizeInput, + extra_headers: HeaderMap, + ) -> Result, ApiError> { + let body = to_value(input).map_err(|e| { + ApiError::Stream(format!( + "failed to encode memory trace summarize input: {e}" + )) + })?; + self.trace_summarize(body, extra_headers).await + } +} + +#[derive(Debug, Deserialize)] +struct TraceSummarizeResponse { + output: Vec, +} + +#[cfg(test)] +mod tests { + use super::*; + use async_trait::async_trait; + use codex_client::Request; + use codex_client::Response; + use codex_client::StreamResponse; + use codex_client::TransportError; + + #[derive(Clone, Default)] + struct DummyTransport; + + #[async_trait] + impl HttpTransport for DummyTransport { + async fn execute(&self, _req: Request) -> Result { + Err(TransportError::Build("execute should not run".to_string())) + } + + async fn stream(&self, _req: Request) -> Result { + Err(TransportError::Build("stream should not run".to_string())) + } + } + + #[derive(Clone, Default)] + struct DummyAuth; + + impl AuthProvider for DummyAuth { + fn bearer_token(&self) -> Option { + None + } + } + + #[test] + fn path_is_memories_trace_summarize() { + assert_eq!( + MemoriesClient::::path(), + "memories/trace_summarize" + ); + } +} diff --git a/codex-rs/codex-api/src/endpoint/mod.rs b/codex-rs/codex-api/src/endpoint/mod.rs index 23579ffcf1..0dede138e8 100644 --- a/codex-rs/codex-api/src/endpoint/mod.rs +++ b/codex-rs/codex-api/src/endpoint/mod.rs @@ -1,5 +1,6 @@ pub mod aggregate; pub mod compact; +pub mod memories; pub mod models; pub mod responses; pub mod responses_websocket; diff --git a/codex-rs/codex-api/src/endpoint/responses_websocket.rs b/codex-rs/codex-api/src/endpoint/responses_websocket.rs index cac686dd02..20c6067b17 100644 --- a/codex-rs/codex-api/src/endpoint/responses_websocket.rs +++ b/codex-rs/codex-api/src/endpoint/responses_websocket.rs @@ -5,6 +5,7 @@ use crate::common::ResponseStream; use crate::common::ResponsesWsRequest; use crate::error::ApiError; use crate::provider::Provider; +use crate::rate_limits::parse_rate_limit_event; use crate::sse::responses::ResponsesStreamEvent; use crate::sse::responses::process_responses_event; use crate::telemetry::WebsocketTelemetry; @@ -33,6 +34,7 @@ use url::Url; type WsStream = WebSocketStream>; const X_CODEX_TURN_STATE_HEADER: &str = "x-codex-turn-state"; +const X_MODELS_ETAG_HEADER: &str = "x-models-etag"; const X_REASONING_INCLUDED_HEADER: &str = "x-reasoning-included"; pub struct ResponsesWebsocketConnection { @@ -40,6 +42,7 @@ pub struct ResponsesWebsocketConnection { // TODO (pakrym): is this the right place for timeout? idle_timeout: Duration, server_reasoning_included: bool, + models_etag: Option, telemetry: Option>, } @@ -48,12 +51,14 @@ impl ResponsesWebsocketConnection { stream: WsStream, idle_timeout: Duration, server_reasoning_included: bool, + models_etag: Option, telemetry: Option>, ) -> Self { Self { stream: Arc::new(Mutex::new(Some(stream))), idle_timeout, server_reasoning_included, + models_etag, telemetry, } } @@ -71,12 +76,16 @@ impl ResponsesWebsocketConnection { let stream = Arc::clone(&self.stream); let idle_timeout = self.idle_timeout; let server_reasoning_included = self.server_reasoning_included; + let models_etag = self.models_etag.clone(); let telemetry = self.telemetry.clone(); let request_body = serde_json::to_value(&request).map_err(|err| { ApiError::Stream(format!("failed to encode websocket request: {err}")) })?; tokio::spawn(async move { + if let Some(etag) = models_etag { + let _ = tx_event.send(Ok(ResponseEvent::ModelsEtag(etag))).await; + } if server_reasoning_included { let _ = tx_event .send(Ok(ResponseEvent::ServerReasoningIncluded(true))) @@ -136,12 +145,13 @@ impl ResponsesWebsocketClient { headers.extend(extra_headers); add_auth_headers_to_header_map(&self.auth, &mut headers); - let (stream, server_reasoning_included) = - connect_websocket(ws_url, headers, turn_state).await?; + let (stream, server_reasoning_included, models_etag) = + connect_websocket(ws_url, headers, turn_state.clone()).await?; Ok(ResponsesWebsocketConnection::new( stream, self.provider.stream_idle_timeout, server_reasoning_included, + models_etag, telemetry, )) } @@ -151,7 +161,7 @@ async fn connect_websocket( url: Url, headers: HeaderMap, turn_state: Option>>, -) -> Result<(WsStream, bool), ApiError> { +) -> Result<(WsStream, bool, Option), ApiError> { info!("connecting to websocket: {url}"); let mut request = url @@ -177,6 +187,11 @@ async fn connect_websocket( }; let reasoning_included = response.headers().contains_key(X_REASONING_INCLUDED_HEADER); + let models_etag = response + .headers() + .get(X_MODELS_ETAG_HEADER) + .and_then(|value| value.to_str().ok()) + .map(ToString::to_string); if let Some(turn_state) = turn_state && let Some(header_value) = response .headers() @@ -185,7 +200,7 @@ async fn connect_websocket( { let _ = turn_state.set(header_value.to_string()); } - Ok((stream, reasoning_included)) + Ok((stream, reasoning_included, models_etag)) } fn map_ws_error(err: WsError, url: &Url) -> ApiError { @@ -273,6 +288,12 @@ async fn run_websocket_response_stream( continue; } }; + if event.kind() == "codex.rate_limits" { + if let Some(snapshot) = parse_rate_limit_event(&text) { + let _ = tx_event.send(Ok(ResponseEvent::RateLimits(snapshot))).await; + } + continue; + } match process_responses_event(event) { Ok(Some(event)) => { let is_completed = matches!(event, ResponseEvent::Completed { .. }); diff --git a/codex-rs/codex-api/src/lib.rs b/codex-rs/codex-api/src/lib.rs index b0c70084d4..70652d2d78 100644 --- a/codex-rs/codex-api/src/lib.rs +++ b/codex-rs/codex-api/src/lib.rs @@ -15,6 +15,10 @@ pub use codex_client::TransportError; pub use crate::auth::AuthProvider; pub use crate::common::CompactionInput; +pub use crate::common::MemoryTrace; +pub use crate::common::MemoryTraceMetadata; +pub use crate::common::MemoryTraceSummarizeInput; +pub use crate::common::MemoryTraceSummaryOutput; pub use crate::common::Prompt; pub use crate::common::ResponseAppendWsRequest; pub use crate::common::ResponseCreateWsRequest; @@ -24,6 +28,7 @@ pub use crate::common::ResponsesApiRequest; pub use crate::common::create_text_param_for_request; pub use crate::endpoint::aggregate::AggregateStreamExt; pub use crate::endpoint::compact::CompactClient; +pub use crate::endpoint::memories::MemoriesClient; pub use crate::endpoint::models::ModelsClient; pub use crate::endpoint::responses::ResponsesClient; pub use crate::endpoint::responses::ResponsesOptions; diff --git a/codex-rs/codex-api/src/rate_limits.rs b/codex-rs/codex-api/src/rate_limits.rs index c29aab21f8..047c5934bd 100644 --- a/codex-rs/codex-api/src/rate_limits.rs +++ b/codex-rs/codex-api/src/rate_limits.rs @@ -1,7 +1,9 @@ +use codex_protocol::account::PlanType; use codex_protocol::protocol::CreditsSnapshot; use codex_protocol::protocol::RateLimitSnapshot; use codex_protocol::protocol::RateLimitWindow; use http::HeaderMap; +use serde::Deserialize; use std::fmt::Display; #[derive(Debug)] @@ -41,6 +43,70 @@ pub fn parse_rate_limit(headers: &HeaderMap) -> Option { }) } +#[derive(Debug, Deserialize)] +struct RateLimitEventWindow { + used_percent: f64, + window_minutes: Option, + reset_at: Option, +} + +#[derive(Debug, Deserialize)] +struct RateLimitEventDetails { + primary: Option, + secondary: Option, +} + +#[derive(Debug, Deserialize)] +struct RateLimitEventCredits { + has_credits: bool, + unlimited: bool, + balance: Option, +} + +#[derive(Debug, Deserialize)] +struct RateLimitEvent { + #[serde(rename = "type")] + kind: String, + plan_type: Option, + rate_limits: Option, + credits: Option, +} + +pub fn parse_rate_limit_event(payload: &str) -> Option { + let event: RateLimitEvent = serde_json::from_str(payload).ok()?; + if event.kind != "codex.rate_limits" { + return None; + } + let (primary, secondary) = if let Some(details) = event.rate_limits.as_ref() { + ( + map_event_window(details.primary.as_ref()), + map_event_window(details.secondary.as_ref()), + ) + } else { + (None, None) + }; + let credits = event.credits.map(|credits| CreditsSnapshot { + has_credits: credits.has_credits, + unlimited: credits.unlimited, + balance: credits.balance, + }); + Some(RateLimitSnapshot { + primary, + secondary, + credits, + plan_type: event.plan_type, + }) +} + +fn map_event_window(window: Option<&RateLimitEventWindow>) -> Option { + let window = window?; + Some(RateLimitWindow { + used_percent: window.used_percent, + window_minutes: window.window_minutes, + resets_at: window.reset_at, + }) +} + /// Parses the bespoke Codex rate-limit headers into a `RateLimitSnapshot`. pub fn parse_promo_message(headers: &HeaderMap) -> Option { parse_header_str(headers, "x-codex-promo-message") diff --git a/codex-rs/codex-api/src/sse/responses.rs b/codex-rs/codex-api/src/sse/responses.rs index 2c91104727..95bda35fb2 100644 --- a/codex-rs/codex-api/src/sse/responses.rs +++ b/codex-rs/codex-api/src/sse/responses.rs @@ -165,6 +165,12 @@ pub struct ResponsesStreamEvent { content_index: Option, } +impl ResponsesStreamEvent { + pub fn kind(&self) -> &str { + &self.kind + } +} + #[derive(Debug)] pub enum ResponsesEventError { Api(ApiError), diff --git a/codex-rs/common/src/config_override.rs b/codex-rs/common/src/config_override.rs index 59dde92a22..9bbec2b6f2 100644 --- a/codex-rs/common/src/config_override.rs +++ b/codex-rs/common/src/config_override.rs @@ -71,7 +71,7 @@ impl CliConfigOverrides { } }; - Ok((key.to_string(), value)) + Ok((canonicalize_override_key(key), value)) }) .collect() } @@ -88,6 +88,14 @@ impl CliConfigOverrides { } } +fn canonicalize_override_key(key: &str) -> String { + if key == "use_linux_sandbox_bwrap" { + "features.use_linux_sandbox_bwrap".to_string() + } else { + key.to_string() + } +} + /// Apply a single override onto `root`, creating intermediate objects as /// necessary. fn apply_single_override(root: &mut Value, path: &str, value: Value) { @@ -172,6 +180,16 @@ mod tests { assert_eq!(arr.len(), 3); } + #[test] + fn canonicalizes_use_linux_sandbox_bwrap_alias() { + let overrides = CliConfigOverrides { + raw_overrides: vec!["use_linux_sandbox_bwrap=true".to_string()], + }; + let parsed = overrides.parse_overrides().expect("parse_overrides"); + assert_eq!(parsed[0].0.as_str(), "features.use_linux_sandbox_bwrap"); + assert_eq!(parsed[0].1.as_bool(), Some(true)); + } + #[test] fn parses_inline_table() { let v = parse_toml_value("{a = 1, b = 2}").expect("parse"); diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index c26ad9dc16..7a178ada44 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -59,6 +59,7 @@ indoc = { workspace = true } keyring = { workspace = true, features = ["crypto-rust"] } libc = { workspace = true } multimap = { workspace = true } +notify = { workspace = true } once_cell = { workspace = true } os_info = { workspace = true } rand = { workspace = true } diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index bddc9d8a0b..e9ac1c3b87 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -190,6 +190,9 @@ "include_apply_patch_tool": { "type": "boolean" }, + "memory_tool": { + "type": "boolean" + }, "personality": { "type": "boolean" }, @@ -235,6 +238,9 @@ "unified_exec": { "type": "boolean" }, + "use_linux_sandbox_bwrap": { + "type": "boolean" + }, "web_search": { "type": "boolean" }, @@ -705,6 +711,7 @@ }, "Personality": { "enum": [ + "none", "friendly", "pragmatic" ], @@ -1042,6 +1049,14 @@ "default": true, "description": "Show startup tooltips in the TUI welcome screen. Defaults to `true`.", "type": "boolean" + }, + "status_line": { + "default": null, + "description": "Ordered list of status line item identifiers.\n\nWhen set, the TUI renders the selected items as the status line.", + "items": { + "type": "string" + }, + "type": "array" } }, "type": "object" @@ -1208,6 +1223,9 @@ "include_apply_patch_tool": { "type": "boolean" }, + "memory_tool": { + "type": "boolean" + }, "personality": { "type": "boolean" }, @@ -1253,6 +1271,9 @@ "unified_exec": { "type": "boolean" }, + "use_linux_sandbox_bwrap": { + "type": "boolean" + }, "web_search": { "type": "boolean" }, @@ -1321,6 +1342,14 @@ "description": "System instructions.", "type": "string" }, + "log_dir": { + "allOf": [ + { + "$ref": "#/definitions/AbsolutePathBuf" + } + ], + "description": "Directory where Codex writes log files, for example `codex-tui.log`. Defaults to `$CODEX_HOME/log`." + }, "mcp_oauth_callback_port": { "description": "Optional fixed port for the local HTTP callback server used during MCP OAuth login. When unset, Codex will bind to an ephemeral port chosen by the OS.", "format": "uint16", diff --git a/codex-rs/core/src/agent/role.rs b/codex-rs/core/src/agent/role.rs index 76e3a373ed..74eca4179d 100644 --- a/codex-rs/core/src/agent/role.rs +++ b/codex-rs/core/src/agent/role.rs @@ -8,7 +8,7 @@ use serde::Serialize; const ORCHESTRATOR_PROMPT: &str = include_str!("../../templates/agents/orchestrator.md"); /// Default model override used. // TODO(jif) update when we have something smarter. -const EXPLORER_MODEL: &str = "gpt-5.2-codex"; +const EXPLORER_MODEL: &str = "gpt-5.1-codex-mini"; /// Enumerated list of all supported agent roles. const ALL_ROLES: [AgentRole; 3] = [ diff --git a/codex-rs/core/src/auth.rs b/codex-rs/core/src/auth.rs index fb4114a7c2..7e58ff125f 100644 --- a/codex-rs/core/src/auth.rs +++ b/codex-rs/core/src/auth.rs @@ -16,6 +16,7 @@ use std::sync::Mutex; use std::sync::RwLock; use codex_app_server_protocol::AuthMode as ApiAuthMode; +use codex_otel::TelemetryAuthMode; use codex_protocol::config_types::ForcedLoginMethod; pub use crate::auth::storage::AuthCredentialsStoreMode; @@ -47,6 +48,15 @@ pub enum AuthMode { Chatgpt, } +impl From for TelemetryAuthMode { + fn from(mode: AuthMode) -> Self { + match mode { + AuthMode::ApiKey => TelemetryAuthMode::ApiKey, + AuthMode::Chatgpt => TelemetryAuthMode::Chatgpt, + } + } +} + /// Authentication mechanism used by the current user. #[derive(Debug, Clone)] pub enum CodexAuth { @@ -186,7 +196,7 @@ impl CodexAuth { load_auth(codex_home, false, auth_credentials_store_mode) } - pub fn internal_auth_mode(&self) -> AuthMode { + pub fn auth_mode(&self) -> AuthMode { match self { Self::ApiKey(_) => AuthMode::ApiKey, Self::Chatgpt(_) | Self::ChatgptAuthTokens(_) => AuthMode::Chatgpt, @@ -202,14 +212,14 @@ impl CodexAuth { } pub fn is_chatgpt_auth(&self) -> bool { - self.internal_auth_mode() == AuthMode::Chatgpt + self.auth_mode() == AuthMode::Chatgpt } pub fn is_external_chatgpt_tokens(&self) -> bool { matches!(self, Self::ChatgptAuthTokens(_)) } - /// Returns `None` is `is_internal_auth_mode() != AuthMode::ApiKey`. + /// Returns `None` if `auth_mode() != AuthMode::ApiKey`. pub fn api_key(&self) -> Option<&str> { match self { Self::ApiKey(auth) => Some(auth.api_key.as_str()), @@ -434,7 +444,7 @@ pub fn enforce_login_restrictions(config: &Config) -> std::io::Result<()> { }; if let Some(required_method) = config.forced_login_method { - let method_violation = match (required_method, auth.internal_auth_mode()) { + let method_violation = match (required_method, auth.auth_mode()) { (ForcedLoginMethod::Api, AuthMode::ApiKey) => None, (ForcedLoginMethod::Chatgpt, AuthMode::Chatgpt) => None, (ForcedLoginMethod::Api, AuthMode::Chatgpt) => Some( @@ -1158,14 +1168,12 @@ impl AuthManager { Ok(removed) } - pub fn get_auth_mode(&self) -> Option { + pub fn get_api_auth_mode(&self) -> Option { self.auth_cached().as_ref().map(CodexAuth::api_auth_mode) } - pub fn get_internal_auth_mode(&self) -> Option { - self.auth_cached() - .as_ref() - .map(CodexAuth::internal_auth_mode) + pub fn auth_mode(&self) -> Option { + self.auth_cached().as_ref().map(CodexAuth::auth_mode) } async fn refresh_if_stale(&self, auth: &CodexAuth) -> Result { @@ -1373,7 +1381,7 @@ mod tests { .unwrap() .unwrap(); assert_eq!(None, auth.api_key()); - assert_eq!(AuthMode::Chatgpt, auth.internal_auth_mode()); + assert_eq!(AuthMode::Chatgpt, auth.auth_mode()); let auth_dot_json = auth .get_current_auth_json() @@ -1418,7 +1426,7 @@ mod tests { let auth = super::load_auth(dir.path(), false, AuthCredentialsStoreMode::File) .unwrap() .unwrap(); - assert_eq!(auth.internal_auth_mode(), AuthMode::ApiKey); + assert_eq!(auth.auth_mode(), AuthMode::ApiKey); assert_eq!(auth.api_key(), Some("sk-test-key")); assert!(auth.get_token_data().is_err()); diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index fcc308f380..b8568a1043 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -1,15 +1,32 @@ -use std::path::PathBuf; +//! Session- and turn-scoped helpers for talking to model provider APIs. +//! +//! `ModelClient` is intended to live for the lifetime of a Codex session and holds the stable +//! configuration and state needed to talk to a provider (auth, provider selection, conversation id, +//! and feature-gated request behavior). +//! +//! Per-turn settings (model selection, reasoning controls, telemetry context, and turn metadata) +//! are passed explicitly to streaming and unary methods so that the turn lifetime is visible at the +//! call site. +//! +//! A [`ModelClientSession`] is created per turn and is used to stream one or more Responses API +//! requests during that turn. It caches a Responses WebSocket connection (opened lazily) and +//! stores per-turn state such as the `x-codex-turn-state` token used for sticky routing. + use std::sync::Arc; use std::sync::OnceLock; -use std::sync::RwLock; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; use crate::api_bridge::CoreAuthProvider; use crate::api_bridge::auth_provider_from_auth; use crate::api_bridge::map_api_error; use crate::auth::UnauthorizedRecovery; -use crate::turn_metadata::build_turn_metadata_header; use codex_api::CompactClient as ApiCompactClient; use codex_api::CompactionInput as ApiCompactionInput; +use codex_api::MemoriesClient as ApiMemoriesClient; +use codex_api::MemoryTrace as ApiMemoryTrace; +use codex_api::MemoryTraceSummarizeInput as ApiMemoryTraceSummarizeInput; +use codex_api::MemoryTraceSummaryOutput as ApiMemoryTraceSummaryOutput; use codex_api::Prompt as ApiPrompt; use codex_api::RequestTelemetry; use codex_api::ReqwestTransport; @@ -32,7 +49,7 @@ use codex_otel::OtelManager; use codex_protocol::ThreadId; use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig; -use codex_protocol::config_types::WebSearchMode; +use codex_protocol::config_types::Verbosity as VerbosityConfig; use codex_protocol::models::ResponseItem; use codex_protocol::openai_models::ModelInfo; use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; @@ -57,53 +74,74 @@ use crate::auth::RefreshTokenError; use crate::client_common::Prompt; use crate::client_common::ResponseEvent; use crate::client_common::ResponseStream; -use crate::config::Config; use crate::default_client::build_reqwest_client; use crate::error::CodexErr; use crate::error::Result; -use crate::features::FEATURES; -use crate::features::Feature; use crate::flags::CODEX_RS_SSE_FIXTURE; use crate::model_provider_info::ModelProviderInfo; use crate::model_provider_info::WireApi; use crate::tools::spec::create_tools_json_for_responses_api; -use crate::transport_manager::TransportManager; -pub const WEB_SEARCH_ELIGIBLE_HEADER: &str = "x-oai-web-search-eligible"; +pub const OPENAI_BETA_HEADER: &str = "OpenAI-Beta"; +pub const OPENAI_BETA_RESPONSES_WEBSOCKETS: &str = "responses_websockets=2026-02-04"; pub const X_CODEX_TURN_STATE_HEADER: &str = "x-codex-turn-state"; pub const X_CODEX_TURN_METADATA_HEADER: &str = "x-codex-turn-metadata"; +pub const X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER: &str = + "x-responsesapi-include-timing-metrics"; -#[derive(Debug, Default)] -struct TurnMetadataCache { - cwd: Option, - header: Option, -} - +/// Session-scoped state shared by all [`ModelClient`] clones. +/// +/// This is intentionally kept minimal so `ModelClient` does not need to hold a full `Config`. Most +/// configuration is per turn and is passed explicitly to streaming/unary methods. #[derive(Debug)] struct ModelClientState { - config: Arc, auth_manager: Option>, - model_info: ModelInfo, - otel_manager: OtelManager, - provider: ModelProviderInfo, conversation_id: ThreadId, - effort: Option, - summary: ReasoningSummaryConfig, + provider: ModelProviderInfo, session_source: SessionSource, - transport_manager: TransportManager, - turn_metadata_cache: Arc>, + model_verbosity: Option, + enable_responses_websockets: bool, + enable_request_compression: bool, + include_timing_metrics: bool, + beta_features_header: Option, + disable_websockets: AtomicBool, } +/// A session-scoped client for model-provider API calls. +/// +/// This holds configuration and state that should be shared across turns within a Codex session +/// (auth, provider selection, conversation id, feature-gated request behavior, and transport +/// fallback state). +/// +/// WebSocket fallback is session-scoped: once a turn activates the HTTP fallback, subsequent turns +/// will also use HTTP for the remainder of the session. +/// +/// Turn-scoped settings (model selection, reasoning controls, telemetry context, and turn metadata) +/// are passed explicitly to the relevant methods to keep turn lifetime visible at the call site. +/// +/// This type is cheap to clone. #[derive(Debug, Clone)] pub struct ModelClient { state: Arc, } +/// A turn-scoped streaming session created from a [`ModelClient`]. +/// +/// The session lazily establishes a Responses WebSocket connection (and reuses it across multiple +/// requests) and caches per-turn state: +/// +/// - The last request's input items, so subsequent calls can use `response.append` when the input +/// is an incremental extension of the previous request. +/// - The `x-codex-turn-state` sticky-routing token, which must be replayed for all requests within +/// the same turn. +/// +/// Create a fresh `ModelClientSession` for each Codex turn. Reusing it across turns would replay +/// the previous turn's sticky-routing token into the next turn, which violates the client/server +/// contract and can cause routing bugs. pub struct ModelClientSession { - state: Arc, + client: ModelClient, connection: Option, websocket_last_items: Vec, - transport_manager: TransportManager, /// Turn state for sticky routing. /// /// This is an `OnceLock` that stores the turn state value received from the server @@ -117,142 +155,65 @@ pub struct ModelClientSession { turn_state: Arc>, } -#[allow(clippy::too_many_arguments)] impl ModelClient { + #[allow(clippy::too_many_arguments)] + /// Creates a new session-scoped `ModelClient`. + /// + /// All arguments are expected to be stable for the lifetime of a Codex session. Per-turn values + /// are passed to [`ModelClientSession::stream`] (and other turn-scoped methods) explicitly. pub fn new( - config: Arc, auth_manager: Option>, - model_info: ModelInfo, - otel_manager: OtelManager, - provider: ModelProviderInfo, - effort: Option, - summary: ReasoningSummaryConfig, conversation_id: ThreadId, + provider: ModelProviderInfo, session_source: SessionSource, - transport_manager: TransportManager, + model_verbosity: Option, + enable_responses_websockets: bool, + enable_request_compression: bool, + include_timing_metrics: bool, + beta_features_header: Option, ) -> Self { Self { state: Arc::new(ModelClientState { - config, auth_manager, - model_info, - otel_manager, - provider, conversation_id, - effort, - summary, + provider, session_source, - transport_manager, - turn_metadata_cache: Arc::new(RwLock::new(TurnMetadataCache::default())), + model_verbosity, + enable_responses_websockets, + enable_request_compression, + include_timing_metrics, + beta_features_header, + disable_websockets: AtomicBool::new(false), }), } } - pub fn new_session(&self, turn_metadata_cwd: Option) -> ModelClientSession { - self.prewarm_turn_metadata_header(turn_metadata_cwd); + /// Creates a fresh turn-scoped streaming session. + /// + /// This does not open any network connections; the WebSocket connection is established lazily + /// when the first WebSocket stream request is issued. + pub fn new_session(&self) -> ModelClientSession { ModelClientSession { - state: Arc::clone(&self.state), + client: self.clone(), connection: None, websocket_last_items: Vec::new(), - transport_manager: self.state.transport_manager.clone(), turn_state: Arc::new(OnceLock::new()), } } - /// Refresh turn metadata in the background and update a cached header that request - /// builders can read without blocking. - fn prewarm_turn_metadata_header(&self, turn_metadata_cwd: Option) { - let turn_metadata_cwd = - turn_metadata_cwd.map(|cwd| std::fs::canonicalize(&cwd).unwrap_or(cwd)); - - if let Ok(mut cache) = self.state.turn_metadata_cache.write() - && cache.cwd != turn_metadata_cwd - { - cache.cwd = turn_metadata_cwd.clone(); - cache.header = None; - } - - let Some(cwd) = turn_metadata_cwd else { - return; - }; - let turn_metadata_cache = Arc::clone(&self.state.turn_metadata_cache); - if let Ok(handle) = tokio::runtime::Handle::try_current() { - let _task = handle.spawn(async move { - let header = build_turn_metadata_header(cwd.as_path()) - .await - .and_then(|value| HeaderValue::from_str(value.as_str()).ok()); - - if let Ok(mut cache) = turn_metadata_cache.write() - && cache.cwd.as_ref() == Some(&cwd) - { - cache.header = header; - } - }); - } - } -} - -impl ModelClient { - pub fn get_model_context_window(&self) -> Option { - let model_info = &self.state.model_info; - let effective_context_window_percent = model_info.effective_context_window_percent; - model_info.context_window.map(|context_window| { - context_window.saturating_mul(effective_context_window_percent) / 100 - }) - } - - pub fn config(&self) -> Arc { - Arc::clone(&self.state.config) - } - - pub fn provider(&self) -> &ModelProviderInfo { - &self.state.provider - } - - pub fn get_provider(&self) -> ModelProviderInfo { - self.state.provider.clone() - } - - pub fn get_otel_manager(&self) -> OtelManager { - self.state.otel_manager.clone() - } - - pub fn get_session_source(&self) -> SessionSource { - self.state.session_source.clone() - } - - pub(crate) fn transport_manager(&self) -> TransportManager { - self.state.transport_manager.clone() - } - - /// Returns the currently configured model slug. - pub fn get_model(&self) -> String { - self.state.model_info.slug.clone() - } - - pub fn get_model_info(&self) -> ModelInfo { - self.state.model_info.clone() - } - - /// Returns the current reasoning effort setting. - pub fn get_reasoning_effort(&self) -> Option { - self.state.effort - } - - /// Returns the current reasoning summary setting. - pub fn get_reasoning_summary(&self) -> ReasoningSummaryConfig { - self.state.summary - } - - pub fn get_auth_manager(&self) -> Option> { - self.state.auth_manager.clone() - } - /// Compacts the current conversation history using the Compact endpoint. /// /// This is a unary call (no streaming) that returns a new list of /// `ResponseItem`s representing the compacted transcript. - pub async fn compact_conversation_history(&self, prompt: &Prompt) -> Result> { + /// + /// The model selection and telemetry context are passed explicitly to keep `ModelClient` + /// session-scoped. + pub async fn compact_conversation_history( + &self, + prompt: &Prompt, + model_info: &ModelInfo, + otel_manager: &OtelManager, + ) -> Result> { if prompt.input.is_empty() { return Ok(Vec::new()); } @@ -264,20 +225,75 @@ impl ModelClient { let api_provider = self .state .provider - .to_api_provider(auth.as_ref().map(CodexAuth::internal_auth_mode))?; + .to_api_provider(auth.as_ref().map(CodexAuth::auth_mode))?; let api_auth = auth_provider_from_auth(auth.clone(), &self.state.provider)?; let transport = ReqwestTransport::new(build_reqwest_client()); - let request_telemetry = self.build_request_telemetry(); + let request_telemetry = Self::build_request_telemetry(otel_manager); let client = ApiCompactClient::new(transport, api_provider, api_auth) .with_telemetry(Some(request_telemetry)); let instructions = prompt.base_instructions.text.clone(); let payload = ApiCompactionInput { - model: &self.state.model_info.slug, + model: &model_info.slug, input: &prompt.input, instructions: &instructions, }; + let extra_headers = self.build_subagent_headers(); + client + .compact_input(&payload, extra_headers) + .await + .map_err(map_api_error) + } + + /// Builds memory summaries for each provided normalized trace. + /// + /// This is a unary call (no streaming) to `/v1/memories/trace_summarize`. + /// + /// The model selection, reasoning effort, and telemetry context are passed explicitly to keep + /// `ModelClient` session-scoped. + pub async fn summarize_memory_traces( + &self, + traces: Vec, + model_info: &ModelInfo, + effort: Option, + otel_manager: &OtelManager, + ) -> Result> { + if traces.is_empty() { + return Ok(Vec::new()); + } + + let auth_manager = self.state.auth_manager.clone(); + let auth = match auth_manager.as_ref() { + Some(manager) => manager.auth().await, + None => None, + }; + let api_provider = self + .state + .provider + .to_api_provider(auth.as_ref().map(CodexAuth::auth_mode))?; + let api_auth = auth_provider_from_auth(auth, &self.state.provider)?; + let transport = ReqwestTransport::new(build_reqwest_client()); + let request_telemetry = Self::build_request_telemetry(otel_manager); + let client = ApiMemoriesClient::new(transport, api_provider, api_auth) + .with_telemetry(Some(request_telemetry)); + + let payload = ApiMemoryTraceSummarizeInput { + model: model_info.slug.clone(), + traces, + reasoning: effort.map(|effort| Reasoning { + effort: Some(effort), + summary: None, + }), + }; + + client + .trace_summarize_input(&payload, self.build_subagent_headers()) + .await + .map_err(map_api_error) + } + + fn build_subagent_headers(&self) -> ApiHeaderMap { let mut extra_headers = ApiHeaderMap::new(); if let SessionSource::SubAgent(sub) = &self.state.session_source { let subagent = match sub { @@ -290,89 +306,63 @@ impl ModelClient { extra_headers.insert("x-openai-subagent", val); } } - client - .compact_input(&payload, extra_headers) - .await - .map_err(map_api_error) + extra_headers + } + + /// Builds request telemetry for unary API calls (e.g., Compact endpoint). + fn build_request_telemetry(otel_manager: &OtelManager) -> Arc { + let telemetry = Arc::new(ApiTelemetry::new(otel_manager.clone())); + let request_telemetry: Arc = telemetry; + request_telemetry } } impl ModelClientSession { - fn turn_metadata_header(&self) -> Option { - self.state - .turn_metadata_cache - .try_read() - .ok() - .and_then(|cache| cache.header.clone()) + fn disable_websockets(&self) -> bool { + self.client.state.disable_websockets.load(Ordering::Relaxed) } - /// Streams a single model turn using the configured Responses transport. - pub async fn stream(&mut self, prompt: &Prompt) -> Result { - let wire_api = self.state.provider.wire_api; - match wire_api { - WireApi::Responses => { - let websocket_enabled = self.responses_websocket_enabled() - && !self.transport_manager.disable_websockets(); - - if websocket_enabled { - self.stream_responses_websocket(prompt).await - } else { - self.stream_responses_api(prompt).await - } - } - } - } - - pub(crate) fn try_switch_fallback_transport(&mut self) -> bool { - let websocket_enabled = self.responses_websocket_enabled(); - let activated = self - .transport_manager - .activate_http_fallback(websocket_enabled); - if activated { - warn!("falling back to HTTP"); - self.state.otel_manager.counter( - "codex.transport.fallback_to_http", - 1, - &[("from_wire_api", "responses_websocket")], - ); - - self.connection = None; - self.websocket_last_items.clear(); - } - activated + fn activate_http_fallback(&self, websocket_enabled: bool) -> bool { + websocket_enabled + && !self + .client + .state + .disable_websockets + .swap(true, Ordering::Relaxed) } fn responses_websocket_enabled(&self) -> bool { - self.state.provider.supports_websockets - && self - .state - .config - .features - .enabled(Feature::ResponsesWebsockets) + self.client.state.provider.supports_websockets + && self.client.state.enable_responses_websockets } - fn build_responses_request(&self, prompt: &Prompt) -> Result { + fn build_responses_request(prompt: &Prompt) -> Result { let instructions = prompt.base_instructions.text.clone(); let tools_json: Vec = create_tools_json_for_responses_api(&prompt.tools)?; Ok(build_api_prompt(prompt, instructions, tools_json)) } + #[allow(clippy::too_many_arguments)] fn build_responses_options( &self, prompt: &Prompt, + model_info: &ModelInfo, + effort: Option, + summary: ReasoningSummaryConfig, + turn_metadata_header: Option<&str>, compression: Compression, ) -> ApiResponsesOptions { - let turn_metadata_header = self.turn_metadata_header(); - let model_info = &self.state.model_info; + let turn_metadata_header = + turn_metadata_header.and_then(|value| HeaderValue::from_str(value).ok()); let default_reasoning_effort = model_info.default_reasoning_level; let reasoning = if model_info.supports_reasoning_summaries { Some(Reasoning { - effort: self.state.effort.or(default_reasoning_effort), - summary: if self.state.summary == ReasoningSummaryConfig::None { + effort: effort.or(default_reasoning_effort), + summary: if summary == ReasoningSummaryConfig::None { None } else { - Some(self.state.summary) + Some(summary) }, }) } else { @@ -386,12 +376,12 @@ impl ModelClientSession { }; let verbosity = if model_info.support_verbosity { - self.state - .config + self.client + .state .model_verbosity .or(model_info.default_verbosity) } else { - if self.state.config.model_verbosity.is_some() { + if self.client.state.model_verbosity.is_some() { warn!( "model_verbosity is set but ignored as the model does not support verbosity: {}", model_info.slug @@ -401,7 +391,7 @@ impl ModelClientSession { }; let text = create_text_param_for_request(verbosity, &prompt.output_schema); - let conversation_id = self.state.conversation_id.to_string(); + let conversation_id = self.client.state.conversation_id.to_string(); ApiResponsesOptions { reasoning, @@ -410,9 +400,9 @@ impl ModelClientSession { text, store_override: None, conversation_id: Some(conversation_id), - session_source: Some(self.state.session_source.clone()), + session_source: Some(self.client.state.session_source.clone()), extra_headers: build_responses_headers( - &self.state.config, + self.client.state.beta_features_header.as_deref(), Some(&self.turn_state), turn_metadata_header.as_ref(), ), @@ -438,6 +428,7 @@ impl ModelClientSession { fn prepare_websocket_request( &self, + model_slug: &str, api_prompt: &ApiPrompt, options: &ApiResponsesOptions, ) -> ResponsesWsRequest { @@ -458,7 +449,7 @@ impl ModelClientSession { let store = store_override.unwrap_or(false); let payload = ResponseCreateWsRequest { - model: self.state.model_info.slug.clone(), + model: model_slug.to_string(), instructions: api_prompt.instructions.clone(), input: api_prompt.input.clone(), tools: api_prompt.tools.clone(), @@ -477,6 +468,7 @@ impl ModelClientSession { async fn websocket_connection( &mut self, + otel_manager: &OtelManager, api_provider: codex_api::Provider, api_auth: CoreAuthProvider, options: &ApiResponsesOptions, @@ -487,9 +479,9 @@ impl ModelClientSession { }; if needs_new { - let mut headers = options.extra_headers.clone(); - headers.extend(build_conversation_headers(options.conversation_id.clone())); - let websocket_telemetry = self.build_websocket_telemetry(); + let headers = + build_websocket_connect_headers(options, self.client.state.include_timing_metrics); + let websocket_telemetry = Self::build_websocket_telemetry(otel_manager); let new_conn: ApiWebSocketConnection = ApiWebSocketResponsesClient::new(api_provider, api_auth) .connect( @@ -507,13 +499,9 @@ impl ModelClientSession { } fn responses_request_compression(&self, auth: Option<&crate::auth::CodexAuth>) -> Compression { - if self - .state - .config - .features - .enabled(Feature::EnableRequestCompression) + if self.client.state.enable_request_compression && auth.is_some_and(CodexAuth::is_chatgpt_auth) - && self.state.provider.is_openai() + && self.client.state.provider.is_openai() { Compression::Zstd } else { @@ -525,17 +513,28 @@ impl ModelClientSession { /// /// Handles SSE fixtures, reasoning summaries, verbosity, and the /// `text` controls used for output schemas. - async fn stream_responses_api(&self, prompt: &Prompt) -> Result { + #[allow(clippy::too_many_arguments)] + async fn stream_responses_api( + &self, + prompt: &Prompt, + model_info: &ModelInfo, + otel_manager: &OtelManager, + effort: Option, + summary: ReasoningSummaryConfig, + turn_metadata_header: Option<&str>, + ) -> Result { if let Some(path) = &*CODEX_RS_SSE_FIXTURE { warn!(path, "Streaming from fixture"); - let stream = - codex_api::stream_from_fixture(path, self.state.provider.stream_idle_timeout()) - .map_err(map_api_error)?; - return Ok(map_response_stream(stream, self.state.otel_manager.clone())); + let stream = codex_api::stream_from_fixture( + path, + self.client.state.provider.stream_idle_timeout(), + ) + .map_err(map_api_error)?; + return Ok(map_response_stream(stream, otel_manager.clone())); } - let auth_manager = self.state.auth_manager.clone(); - let api_prompt = self.build_responses_request(prompt)?; + let auth_manager = self.client.state.auth_manager.clone(); + let api_prompt = Self::build_responses_request(prompt)?; let mut auth_recovery = auth_manager .as_ref() @@ -546,26 +545,34 @@ impl ModelClientSession { None => None, }; let api_provider = self + .client .state .provider - .to_api_provider(auth.as_ref().map(CodexAuth::internal_auth_mode))?; - let api_auth = auth_provider_from_auth(auth.clone(), &self.state.provider)?; + .to_api_provider(auth.as_ref().map(CodexAuth::auth_mode))?; + let api_auth = auth_provider_from_auth(auth.clone(), &self.client.state.provider)?; let transport = ReqwestTransport::new(build_reqwest_client()); - let (request_telemetry, sse_telemetry) = self.build_streaming_telemetry(); + let (request_telemetry, sse_telemetry) = Self::build_streaming_telemetry(otel_manager); let compression = self.responses_request_compression(auth.as_ref()); let client = ApiResponsesClient::new(transport, api_provider, api_auth) .with_telemetry(Some(request_telemetry), Some(sse_telemetry)); - let options = self.build_responses_options(prompt, compression); + let options = self.build_responses_options( + prompt, + model_info, + effort, + summary, + turn_metadata_header, + compression, + ); let stream_result = client - .stream_prompt(&self.state.model_info.slug, &api_prompt, options) + .stream_prompt(&model_info.slug, &api_prompt, options) .await; match stream_result { Ok(stream) => { - return Ok(map_response_stream(stream, self.state.otel_manager.clone())); + return Ok(map_response_stream(stream, otel_manager.clone())); } Err(ApiError::Transport( unauthorized_transport @ TransportError::Http { status, .. }, @@ -579,9 +586,18 @@ impl ModelClientSession { } /// Streams a turn via the Responses API over WebSocket transport. - async fn stream_responses_websocket(&mut self, prompt: &Prompt) -> Result { - let auth_manager = self.state.auth_manager.clone(); - let api_prompt = self.build_responses_request(prompt)?; + #[allow(clippy::too_many_arguments)] + async fn stream_responses_websocket( + &mut self, + prompt: &Prompt, + model_info: &ModelInfo, + otel_manager: &OtelManager, + effort: Option, + summary: ReasoningSummaryConfig, + turn_metadata_header: Option<&str>, + ) -> Result { + let auth_manager = self.client.state.auth_manager.clone(); + let api_prompt = Self::build_responses_request(prompt)?; let mut auth_recovery = auth_manager .as_ref() @@ -592,17 +608,30 @@ impl ModelClientSession { None => None, }; let api_provider = self + .client .state .provider - .to_api_provider(auth.as_ref().map(CodexAuth::internal_auth_mode))?; - let api_auth = auth_provider_from_auth(auth.clone(), &self.state.provider)?; + .to_api_provider(auth.as_ref().map(CodexAuth::auth_mode))?; + let api_auth = auth_provider_from_auth(auth.clone(), &self.client.state.provider)?; let compression = self.responses_request_compression(auth.as_ref()); - let options = self.build_responses_options(prompt, compression); - let request = self.prepare_websocket_request(&api_prompt, &options); + let options = self.build_responses_options( + prompt, + model_info, + effort, + summary, + turn_metadata_header, + compression, + ); + let request = self.prepare_websocket_request(&model_info.slug, &api_prompt, &options); let connection = match self - .websocket_connection(api_provider.clone(), api_auth.clone(), &options) + .websocket_connection( + otel_manager, + api_provider.clone(), + api_auth.clone(), + &options, + ) .await { Ok(connection) => connection, @@ -621,35 +650,94 @@ impl ModelClientSession { .map_err(map_api_error)?; self.websocket_last_items = api_prompt.input.clone(); - return Ok(map_response_stream( - stream_result, - self.state.otel_manager.clone(), - )); + return Ok(map_response_stream(stream_result, otel_manager.clone())); } } /// Builds request and SSE telemetry for streaming API calls. - fn build_streaming_telemetry(&self) -> (Arc, Arc) { - let telemetry = Arc::new(ApiTelemetry::new(self.state.otel_manager.clone())); + fn build_streaming_telemetry( + otel_manager: &OtelManager, + ) -> (Arc, Arc) { + let telemetry = Arc::new(ApiTelemetry::new(otel_manager.clone())); let request_telemetry: Arc = telemetry.clone(); let sse_telemetry: Arc = telemetry; (request_telemetry, sse_telemetry) } /// Builds telemetry for the Responses API WebSocket transport. - fn build_websocket_telemetry(&self) -> Arc { - let telemetry = Arc::new(ApiTelemetry::new(self.state.otel_manager.clone())); + fn build_websocket_telemetry(otel_manager: &OtelManager) -> Arc { + let telemetry = Arc::new(ApiTelemetry::new(otel_manager.clone())); let websocket_telemetry: Arc = telemetry; websocket_telemetry } -} -impl ModelClient { - /// Builds request telemetry for unary API calls (e.g., Compact endpoint). - fn build_request_telemetry(&self) -> Arc { - let telemetry = Arc::new(ApiTelemetry::new(self.state.otel_manager.clone())); - let request_telemetry: Arc = telemetry; - request_telemetry + #[allow(clippy::too_many_arguments)] + /// Streams a single model request within the current turn. + /// + /// The caller is responsible for passing per-turn settings explicitly (model selection, + /// reasoning settings, telemetry context, and turn metadata). This method will prefer the + /// Responses WebSocket transport when enabled and healthy, and will fall back to the HTTP + /// Responses API transport otherwise. + pub async fn stream( + &mut self, + prompt: &Prompt, + model_info: &ModelInfo, + otel_manager: &OtelManager, + effort: Option, + summary: ReasoningSummaryConfig, + turn_metadata_header: Option<&str>, + ) -> Result { + let wire_api = self.client.state.provider.wire_api; + match wire_api { + WireApi::Responses => { + let websocket_enabled = + self.responses_websocket_enabled() && !self.disable_websockets(); + + if websocket_enabled { + self.stream_responses_websocket( + prompt, + model_info, + otel_manager, + effort, + summary, + turn_metadata_header, + ) + .await + } else { + self.stream_responses_api( + prompt, + model_info, + otel_manager, + effort, + summary, + turn_metadata_header, + ) + .await + } + } + } + } + + /// Permanently disables WebSockets for this Codex session and resets WebSocket state. + /// + /// This is used after exhausting the provider retry budget, to force subsequent requests onto + /// the HTTP transport. Returns `true` if this call activated fallback, or `false` if fallback + /// was already active. + pub(crate) fn try_switch_fallback_transport(&mut self, otel_manager: &OtelManager) -> bool { + let websocket_enabled = self.responses_websocket_enabled(); + let activated = self.activate_http_fallback(websocket_enabled); + if activated { + warn!("falling back to HTTP"); + otel_manager.counter( + "codex.transport.fallback_to_http", + 1, + &[("from_wire_api", "responses_websocket")], + ); + + self.connection = None; + self.websocket_last_items.clear(); + } + activated } } @@ -664,45 +752,25 @@ fn build_api_prompt(prompt: &Prompt, instructions: String, tools_json: Vec ApiHeaderMap { - let enabled = FEATURES - .iter() - .filter_map(|spec| { - if spec.stage.experimental_menu_description().is_some() - && config.features.enabled(spec.id) - { - Some(spec.key) - } else { - None - } - }) - .collect::>(); - let value = enabled.join(","); - let mut headers = ApiHeaderMap::new(); - if !value.is_empty() - && let Ok(header_value) = HeaderValue::from_str(value.as_str()) - { - headers.insert("x-codex-beta-features", header_value); - } - headers -} - +/// Builds the extra headers attached to Responses API requests. +/// +/// These headers implement Codex-specific conventions: +/// +/// - `x-codex-beta-features`: comma-separated beta feature keys enabled for the session. +/// - `x-codex-turn-state`: sticky routing token captured earlier in the turn. +/// - `x-codex-turn-metadata`: optional per-turn metadata for observability. fn build_responses_headers( - config: &Config, + beta_features_header: Option<&str>, turn_state: Option<&Arc>>, turn_metadata_header: Option<&HeaderValue>, ) -> ApiHeaderMap { - let mut headers = experimental_feature_headers(config); - headers.insert( - WEB_SEARCH_ELIGIBLE_HEADER, - HeaderValue::from_static( - if matches!(config.web_search_mode, Some(WebSearchMode::Disabled)) { - "false" - } else { - "true" - }, - ), - ); + let mut headers = ApiHeaderMap::new(); + if let Some(value) = beta_features_header + && !value.is_empty() + && let Ok(header_value) = HeaderValue::from_str(value) + { + headers.insert("x-codex-beta-features", header_value); + } if let Some(turn_state) = turn_state && let Some(state) = turn_state.get() && let Ok(header_value) = HeaderValue::from_str(state) @@ -715,6 +783,25 @@ fn build_responses_headers( headers } +fn build_websocket_connect_headers( + options: &ApiResponsesOptions, + include_timing_metrics: bool, +) -> ApiHeaderMap { + let mut headers = options.extra_headers.clone(); + headers.extend(build_conversation_headers(options.conversation_id.clone())); + headers.insert( + OPENAI_BETA_HEADER, + HeaderValue::from_static(OPENAI_BETA_RESPONSES_WEBSOCKETS), + ); + if include_timing_metrics { + headers.insert( + X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER, + HeaderValue::from_static("true"), + ); + } + headers +} + fn map_response_stream(api_stream: S, otel_manager: OtelManager) -> ResponseStream where S: futures::Stream> diff --git a/codex-rs/core/src/client_common.rs b/codex-rs/core/src/client_common.rs index 2614ce83ef..5a4eea8836 100644 --- a/codex-rs/core/src/client_common.rs +++ b/codex-rs/core/src/client_common.rs @@ -3,6 +3,7 @@ use crate::config::types::Personality; use crate::error::Result; pub use codex_api::common::ResponseEvent; use codex_protocol::models::BaseInstructions; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::ResponseItem; use futures::Stream; use serde::Deserialize; @@ -97,9 +98,11 @@ fn reserialize_shell_outputs(items: &mut [ResponseItem]) { } ResponseItem::FunctionCallOutput { call_id, output } => { if shell_call_ids.remove(call_id) - && let Some(structured) = parse_structured_shell_output(&output.content) + && let Some(structured) = output + .text_content() + .and_then(parse_structured_shell_output) { - output.content = structured + output.body = FunctionCallOutputBody::Text(structured); } } _ => {} diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index b7c0404056..572093d84a 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -20,9 +20,13 @@ use crate::compact::should_use_remote_compact_task; use crate::compact_remote::run_inline_remote_auto_compact_task; use crate::connectors; use crate::exec_policy::ExecPolicyManager; +use crate::features::FEATURES; use crate::features::Feature; use crate::features::Features; use crate::features::maybe_push_unstable_features_warning; +use crate::hooks::HookEvent; +use crate::hooks::HookEventAfterAgent; +use crate::hooks::Hooks; use crate::models_manager::manager::ModelsManager; use crate::parse_command::parse_command; use crate::parse_turn_item; @@ -32,9 +36,8 @@ use crate::stream_events_utils::handle_non_tool_response_item; use crate::stream_events_utils::handle_output_item_done; use crate::stream_events_utils::last_assistant_message_from_item; use crate::terminal; -use crate::transport_manager::TransportManager; use crate::truncate::TruncationPolicy; -use crate::user_notification::UserNotifier; +use crate::turn_metadata::build_turn_metadata_header; use crate::util::error_or_panic; use async_channel::Receiver; use async_channel::Sender; @@ -80,6 +83,7 @@ use rmcp::model::RequestId; use serde_json; use serde_json::Value; use tokio::sync::Mutex; +use tokio::sync::OnceCell; use tokio::sync::RwLock; use tokio::sync::oneshot; use tokio_util::sync::CancellationToken; @@ -90,6 +94,7 @@ use tracing::field; use tracing::info; use tracing::info_span; use tracing::instrument; +use tracing::trace; use tracing::trace_span; use tracing::warn; @@ -113,8 +118,17 @@ use crate::error::CodexErr; use crate::error::Result as CodexResult; #[cfg(test)] use crate::exec::StreamOutput; + +#[derive(Debug, PartialEq)] +pub enum SteerInputError { + NoActiveTurn(Vec), + ExpectedTurnMismatch { expected: String, actual: String }, + EmptyInput, +} use crate::exec_policy::ExecPolicyUpdateError; use crate::feedback_tags; +use crate::file_watcher::FileWatcher; +use crate::file_watcher::FileWatcherEvent; use crate::git_info::get_git_repo_root; use crate::instructions::UserInstructions; use crate::mcp::CODEX_APPS_MCP_SERVER_NAME; @@ -196,11 +210,11 @@ use crate::tools::spec::ToolsConfig; use crate::tools::spec::ToolsConfigParams; use crate::turn_diff_tracker::TurnDiffTracker; use crate::unified_exec::UnifiedExecProcessManager; -use crate::user_notification::UserNotification; use crate::util::backoff; use crate::windows_sandbox::WindowsSandboxLevelExt; use codex_async_utils::OrCancelExt; use codex_otel::OtelManager; +use codex_otel::TelemetryAuthMode; use codex_protocol::config_types::CollaborationMode; use codex_protocol::config_types::Personality; use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig; @@ -209,6 +223,7 @@ use codex_protocol::models::ContentItem; use codex_protocol::models::DeveloperInstructions; use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ResponseItem; +use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; use codex_protocol::protocol::CodexErrorInfo; use codex_protocol::protocol::InitialHistory; use codex_protocol::user_input::UserInput; @@ -248,6 +263,7 @@ impl Codex { auth_manager: Arc, models_manager: Arc, skills_manager: Arc, + file_watcher: Arc, conversation_history: InitialHistory, session_source: SessionSource, agent_control: AgentControl, @@ -384,6 +400,7 @@ impl Codex { conversation_history, session_source_clone, skills_manager, + file_watcher, agent_control, ) .instrument(session_init_span) @@ -445,6 +462,14 @@ impl Codex { Ok(event) } + pub async fn steer_input( + &self, + input: Vec, + expected_turn_id: Option<&str>, + ) -> Result { + self.session.steer_input(input, expected_turn_id).await + } + pub(crate) async fn agent_status(&self) -> AgentStatus { self.agent_status.borrow().clone() } @@ -480,7 +505,14 @@ pub(crate) struct Session { #[derive(Debug)] pub(crate) struct TurnContext { pub(crate) sub_id: String, - pub(crate) client: ModelClient, + pub(crate) config: Arc, + pub(crate) auth_manager: Option>, + pub(crate) model_info: ModelInfo, + pub(crate) otel_manager: OtelManager, + pub(crate) provider: ModelProviderInfo, + pub(crate) reasoning_effort: Option, + pub(crate) reasoning_summary: ReasoningSummaryConfig, + pub(crate) session_source: SessionSource, /// The session's current working directory. All relative paths provided by /// the model as well as sandbox policies are resolved against this path /// instead of `std::env::current_dir()`. @@ -495,14 +527,23 @@ pub(crate) struct TurnContext { pub(crate) windows_sandbox_level: WindowsSandboxLevel, pub(crate) shell_environment_policy: ShellEnvironmentPolicy, pub(crate) tools_config: ToolsConfig, + pub(crate) features: Features, pub(crate) ghost_snapshot: GhostSnapshotConfig, pub(crate) final_output_json_schema: Option, pub(crate) codex_linux_sandbox_exe: Option, pub(crate) tool_call_gate: Arc, pub(crate) truncation_policy: TruncationPolicy, pub(crate) dynamic_tools: Vec, + turn_metadata_header: OnceCell>, } impl TurnContext { + pub(crate) fn model_context_window(&self) -> Option { + let effective_context_window_percent = self.model_info.effective_context_window_percent; + self.model_info.context_window.map(|context_window| { + context_window.saturating_mul(effective_context_window_percent) / 100 + }) + } + pub(crate) fn resolve_path(&self, path: Option) -> PathBuf { path.as_ref() .map(PathBuf::from) @@ -514,6 +555,38 @@ impl TurnContext { .as_deref() .unwrap_or(compact::SUMMARIZATION_PROMPT) } + + async fn build_turn_metadata_header(&self) -> Option { + self.turn_metadata_header + .get_or_init(|| async { build_turn_metadata_header(self.cwd.as_path()).await }) + .await + .clone() + } + + pub async fn resolve_turn_metadata_header(&self) -> Option { + const TURN_METADATA_HEADER_TIMEOUT_MS: u64 = 250; + match tokio::time::timeout( + std::time::Duration::from_millis(TURN_METADATA_HEADER_TIMEOUT_MS), + self.build_turn_metadata_header(), + ) + .await + { + Ok(header) => header, + Err(_) => { + warn!("timed out after 250ms while building turn metadata header"); + self.turn_metadata_header.get().cloned().flatten() + } + } + } + + pub fn spawn_turn_metadata_header_task(self: &Arc) { + let context = Arc::clone(self); + tokio::spawn(async move { + trace!("Spawning turn metadata calculation task"); + context.build_turn_metadata_header().await; + trace!("Turn metadata calculation task completed"); + }); + } } #[derive(Clone)] @@ -623,6 +696,33 @@ pub(crate) struct SessionSettingsUpdate { } impl Session { + /// Builds the `x-codex-beta-features` header value for this session. + /// + /// `ModelClient` is session-scoped and intentionally does not depend on the full `Config`, so + /// we precompute the comma-separated list of enabled experimental feature keys at session + /// creation time and thread it into the client. + fn build_model_client_beta_features_header(config: &Config) -> Option { + let beta_features_header = FEATURES + .iter() + .filter_map(|spec| { + if spec.stage.experimental_menu_description().is_some() + && config.features.enabled(spec.id) + { + Some(spec.key) + } else { + None + } + }) + .collect::>() + .join(","); + + if beta_features_header.is_empty() { + None + } else { + Some(beta_features_header) + } + } + /// Don't expand the number of mutated arguments on config. We are in the process of getting rid of it. pub(crate) fn build_per_turn_config(session_configuration: &SessionConfiguration) -> Config { // todo(aibrahim): store this state somewhere else so we don't need to mut config @@ -634,7 +734,6 @@ impl Session { per_turn_config.personality = session_configuration.personality; per_turn_config.web_search_mode = Some(resolve_web_search_mode_for_turn( per_turn_config.web_search_mode, - session_configuration.provider.is_azure_responses_endpoint(), session_configuration.sandbox_policy.get(), )); per_turn_config.features = config.features.clone(); @@ -646,6 +745,29 @@ impl Session { state.session_configuration.codex_home().clone() } + fn start_file_watcher_listener(self: &Arc) { + let mut rx = self.services.file_watcher.subscribe(); + let weak_sess = Arc::downgrade(self); + tokio::spawn(async move { + loop { + match rx.recv().await { + Ok(FileWatcherEvent::SkillsChanged { .. }) => { + let Some(sess) = weak_sess.upgrade() else { + break; + }; + let event = Event { + id: sess.next_internal_sub_id(), + msg: EventMsg::SkillsUpdateAvailable, + }; + sess.send_event_raw(event).await; + } + Err(tokio::sync::broadcast::error::RecvError::Closed) => break, + Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => continue, + } + } + }); + } + #[allow(clippy::too_many_arguments)] fn make_turn_context( auth_manager: Option>, @@ -654,27 +776,19 @@ impl Session { session_configuration: &SessionConfiguration, per_turn_config: Config, model_info: ModelInfo, - conversation_id: ThreadId, sub_id: String, - transport_manager: TransportManager, ) -> TurnContext { + let reasoning_effort = session_configuration.collaboration_mode.reasoning_effort(); + let reasoning_summary = session_configuration.model_reasoning_summary; let otel_manager = otel_manager.clone().with_model( session_configuration.collaboration_mode.model(), model_info.slug.as_str(), ); + let session_source = session_configuration.session_source.clone(); + let auth_manager_for_context = auth_manager; + let provider_for_context = provider; + let otel_manager_for_context = otel_manager; let per_turn_config = Arc::new(per_turn_config); - let client = ModelClient::new( - per_turn_config.clone(), - auth_manager, - model_info.clone(), - otel_manager, - provider, - session_configuration.collaboration_mode.reasoning_effort(), - session_configuration.model_reasoning_summary, - conversation_id, - session_configuration.session_source.clone(), - transport_manager, - ); let tools_config = ToolsConfig::new(&ToolsConfigParams { model_info: &model_info, @@ -682,10 +796,18 @@ impl Session { web_search_mode: per_turn_config.web_search_mode, }); + let cwd = session_configuration.cwd.clone(); TurnContext { sub_id, - client, - cwd: session_configuration.cwd.clone(), + config: per_turn_config.clone(), + auth_manager: auth_manager_for_context, + model_info: model_info.clone(), + otel_manager: otel_manager_for_context, + provider: provider_for_context, + reasoning_effort, + reasoning_summary, + session_source, + cwd, developer_instructions: session_configuration.developer_instructions.clone(), compact_prompt: session_configuration.compact_prompt.clone(), user_instructions: session_configuration.user_instructions.clone(), @@ -696,12 +818,14 @@ impl Session { windows_sandbox_level: session_configuration.windows_sandbox_level, shell_environment_policy: per_turn_config.shell_environment_policy.clone(), tools_config, + features: per_turn_config.features.clone(), ghost_snapshot: per_turn_config.ghost_snapshot.clone(), final_output_json_schema: None, codex_linux_sandbox_exe: per_turn_config.codex_linux_sandbox_exe.clone(), tool_call_gate: Arc::new(ReadinessFlag::new()), truncation_policy: model_info.truncation_policy.into(), dynamic_tools: session_configuration.dynamic_tools.clone(), + turn_metadata_header: OnceCell::new(), } } @@ -717,6 +841,7 @@ impl Session { initial_history: InitialHistory, session_source: SessionSource, skills_manager: Arc, + file_watcher: Arc, agent_control: AgentControl, ) -> anyhow::Result> { debug!( @@ -836,16 +961,25 @@ impl Session { }), }); } + for message in &config.startup_warnings { + post_session_configured_events.push(Event { + id: "".to_owned(), + msg: EventMsg::Warning(WarningEvent { + message: message.clone(), + }), + }); + } maybe_push_unstable_features_warning(&config, &mut post_session_configured_events); let auth = auth.as_ref(); + let auth_mode = auth.map(CodexAuth::auth_mode).map(TelemetryAuthMode::from); let otel_manager = OtelManager::new( conversation_id, session_configuration.collaboration_mode.model(), session_configuration.collaboration_mode.model(), auth.and_then(CodexAuth::get_account_id), auth.and_then(CodexAuth::get_account_email), - auth.map(CodexAuth::api_auth_mode), + auth_mode, config.otel.log_user_prompt, terminal::user_agent(), session_configuration.session_source.clone(), @@ -906,7 +1040,7 @@ impl Session { Arc::clone(&config), Arc::clone(&auth_manager), ), - notifier: UserNotifier::new(config.notify.clone()), + hooks: Hooks::new(config.as_ref()), rollout: Mutex::new(rollout_recorder), user_shell: Arc::new(default_shell), show_raw_agent_reasoning: config.show_raw_agent_reasoning, @@ -916,9 +1050,20 @@ impl Session { models_manager: Arc::clone(&models_manager), tool_approvals: Mutex::new(ApprovalStore::default()), skills_manager, + file_watcher, agent_control, state_db: state_db_ctx.clone(), - transport_manager: TransportManager::new(), + model_client: ModelClient::new( + Some(Arc::clone(&auth_manager)), + conversation_id, + session_configuration.provider.clone(), + session_configuration.session_source.clone(), + config.model_verbosity, + config.features.enabled(Feature::ResponsesWebsockets), + config.features.enabled(Feature::EnableRequestCompression), + config.features.enabled(Feature::RuntimeMetrics), + Self::build_model_client_beta_features_header(config.as_ref()), + ), }; let sess = Arc::new(Session { @@ -959,12 +1104,16 @@ impl Session { sess.send_event_raw(event).await; } + // Start the watcher after SessionConfigured so it cannot emit earlier events. + sess.start_file_watcher_listener(); + // Construct sandbox_state before initialize() so it can be sent to each // MCP server immediately after it becomes ready (avoiding blocking). let sandbox_state = SandboxState { sandbox_policy: session_configuration.sandbox_policy.get().clone(), codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(), sandbox_cwd: session_configuration.cwd.clone(), + use_linux_sandbox_bwrap: config.features.enabled(Feature::UseLinuxSandboxBwrap), }; let cancel_token = sess.mcp_startup_cancellation_token().await; @@ -1052,32 +1201,26 @@ impl Session { { let mut state = self.state.lock().await; state.initial_context_seeded = false; + state.pending_resume_previous_model = None; } // If resuming, warn when the last recorded model differs from the current one. - if let Some(prev) = rollout_items.iter().rev().find_map(|it| { - if let RolloutItem::TurnContext(ctx) = it { - Some(ctx.model.as_str()) - } else { - None - } - }) { - let curr = turn_context.client.get_model(); - if prev != curr { - warn!( - "resuming session with different model: previous={prev}, current={curr}" - ); - self.send_event( - &turn_context, - EventMsg::Warning(WarningEvent { - message: format!( - "This session was recorded with model `{prev}` but is resuming with `{curr}`. \ + let curr = turn_context.model_info.slug.as_str(); + if let Some(prev) = Self::last_model_name(&rollout_items, curr) { + warn!("resuming session with different model: previous={prev}, current={curr}"); + self.send_event( + &turn_context, + EventMsg::Warning(WarningEvent { + message: format!( + "This session was recorded with model `{prev}` but is resuming with `{curr}`. \ Consider switching back to `{prev}` as it may affect Codex performance." - ), - }), - ) - .await; - } + ), + }), + ) + .await; + + let mut state = self.state.lock().await; + state.pending_resume_previous_model = Some(prev.to_string()); } // Always add response items to conversation history @@ -1136,6 +1279,21 @@ impl Session { } } + fn last_model_name<'a>(rollout_items: &'a [RolloutItem], current: &str) -> Option<&'a str> { + let previous = rollout_items.iter().rev().find_map(|it| { + if let RolloutItem::TurnContext(ctx) = it { + Some(ctx.model.as_str()) + } else { + None + } + })?; + if previous == current { + None + } else { + Some(previous) + } + } + fn last_token_info_from_rollout(rollout_items: &[RolloutItem]) -> Option { rollout_items.iter().rev().find_map(|item| match item { RolloutItem::EventMsg(EventMsg::TokenCount(ev)) => ev.info.clone(), @@ -1143,6 +1301,11 @@ impl Session { }) } + async fn take_pending_resume_previous_model(&self) -> Option { + let mut state = self.state.lock().await; + state.pending_resume_previous_model.take() + } + pub(crate) async fn update_settings( &self, updates: SessionSettingsUpdate, @@ -1214,6 +1377,9 @@ impl Session { sandbox_policy: per_turn_config.sandbox_policy.get().clone(), codex_linux_sandbox_exe: per_turn_config.codex_linux_sandbox_exe.clone(), sandbox_cwd: per_turn_config.cwd.clone(), + use_linux_sandbox_bwrap: per_turn_config + .features + .enabled(Feature::UseLinuxSandboxBwrap), }; if let Err(e) = self .services @@ -1242,14 +1408,15 @@ impl Session { &session_configuration, per_turn_config, model_info, - self.conversation_id, sub_id, - self.services.transport_manager.clone(), ); + if let Some(final_schema) = final_output_json_schema { turn_context.final_output_json_schema = final_schema; } - Arc::new(turn_context) + let turn_context = Arc::new(turn_context); + turn_context.spawn_turn_metadata_header_task(); + turn_context } pub(crate) async fn new_default_turn(&self) -> Arc { @@ -1332,13 +1499,16 @@ impl Session { return None; } let previous = previous?; + if next.model_info.slug != previous.model_info.slug { + return None; + } // if a personality is specified and it's different from the previous one, build a personality update item if let Some(personality) = next.personality && next.personality != previous.personality { - let model_info = next.client.get_model_info(); - let personality_message = Self::personality_message_for(&model_info, personality); + let model_info = &next.model_info; + let personality_message = Self::personality_message_for(model_info, personality); personality_message.map(|personality_message| { DeveloperInstructions::personality_spec_message(personality_message).into() }) @@ -1370,9 +1540,30 @@ impl Session { } } + fn build_model_instructions_update_item( + &self, + previous: Option<&Arc>, + resumed_model: Option<&str>, + next: &TurnContext, + ) -> Option { + let previous_model = + resumed_model.or_else(|| previous.map(|prev| prev.model_info.slug.as_str()))?; + if previous_model == next.model_info.slug { + return None; + } + + let model_instructions = next.model_info.get_model_instructions(next.personality); + if model_instructions.is_empty() { + return None; + } + + Some(DeveloperInstructions::model_switch_message(model_instructions).into()) + } + fn build_settings_update_items( &self, previous_context: Option<&Arc>, + resumed_model: Option<&str>, current_context: &TurnContext, ) -> Vec { let mut update_items = Vec::new(); @@ -1391,6 +1582,13 @@ impl Session { { update_items.push(collaboration_mode_item); } + if let Some(model_instructions_item) = self.build_model_instructions_update_item( + previous_context, + resumed_model, + current_context, + ) { + update_items.push(model_instructions_item); + } if let Some(personality_item) = self.build_personality_update_item(previous_context, current_context) { @@ -1513,6 +1711,17 @@ impl Session { .map(|task| Arc::clone(&task.turn_context)) } + async fn active_turn_context_and_cancellation_token( + &self, + ) -> Option<(Arc, CancellationToken)> { + let active = self.active_turn.lock().await; + let (_, task) = active.as_ref()?.tasks.first()?; + Some(( + Arc::clone(&task.turn_context), + task.cancellation_token.child_token(), + )) + } + pub(crate) async fn record_execpolicy_amendment_message( &self, sub_id: &str, @@ -1902,7 +2111,7 @@ impl Session { if self.features.enabled(Feature::Personality) && let Some(personality) = turn_context.personality { - let model_info = turn_context.client.get_model_info(); + let model_info = turn_context.model_info.clone(); let has_baked_personality = model_info.supports_personality() && base_instructions == model_info.get_model_instructions(Some(personality)); if !has_baked_personality @@ -1955,20 +2164,18 @@ impl Session { { let mut state = self.state.lock().await; if let Some(token_usage) = token_usage { - state.update_token_info_from_usage( - token_usage, - turn_context.client.get_model_context_window(), - ); + state + .update_token_info_from_usage(token_usage, turn_context.model_context_window()); } } self.send_token_count_event(turn_context).await; } pub(crate) async fn recompute_token_usage(&self, turn_context: &TurnContext) { - let Some(estimated_total_tokens) = self - .clone_history() - .await - .estimate_token_count(turn_context) + let history = self.clone_history().await; + let base_instructions = self.get_base_instructions().await; + let Some(estimated_total_tokens) = + history.estimate_token_count_with_base_instructions(&base_instructions) else { return; }; @@ -1989,7 +2196,7 @@ impl Session { }; if info.model_context_window.is_none() { - info.model_context_window = turn_context.client.get_model_context_window(); + info.model_context_window = turn_context.model_context_window(); } state.set_token_info(Some(info)); @@ -2047,7 +2254,7 @@ impl Session { } pub(crate) async fn set_total_tokens_full(&self, turn_context: &TurnContext) { - if let Some(context_window) = turn_context.client.get_model_context_window() { + if let Some(context_window) = turn_context.model_context_window() { let mut state = self.state.lock().await; state.set_token_usage_full(context_window); } @@ -2143,17 +2350,39 @@ impl Session { .await; } - /// Returns the input if there was no task running to inject into - pub async fn inject_input(&self, input: Vec) -> Result<(), Vec> { - let mut active = self.active_turn.lock().await; - match active.as_mut() { - Some(at) => { - let mut ts = at.turn_state.lock().await; - ts.push_pending_input(input.into()); - Ok(()) - } - None => Err(input), + /// Inject additional user input into the currently active turn. + /// + /// Returns the active turn id when accepted. + pub async fn steer_input( + &self, + input: Vec, + expected_turn_id: Option<&str>, + ) -> Result { + if input.is_empty() { + return Err(SteerInputError::EmptyInput); } + + let mut active = self.active_turn.lock().await; + let Some(active_turn) = active.as_mut() else { + return Err(SteerInputError::NoActiveTurn(input)); + }; + + let Some((active_turn_id, _)) = active_turn.tasks.first() else { + return Err(SteerInputError::NoActiveTurn(input)); + }; + + if let Some(expected_turn_id) = expected_turn_id + && expected_turn_id != active_turn_id + { + return Err(SteerInputError::ExpectedTurnMismatch { + expected: expected_turn_id.to_string(), + actual: active_turn_id.clone(), + }); + } + + let mut turn_state = active_turn.turn_state.lock().await; + turn_state.push_pending_input(input.into()); + Ok(active_turn_id.clone()) } /// Returns the input if there was no task running to inject into @@ -2268,8 +2497,8 @@ impl Session { } } - pub(crate) fn notifier(&self) -> &UserNotifier { - &self.services.notifier + pub(crate) fn hooks(&self) -> &Hooks { + &self.services.hooks } pub(crate) fn user_shell(&self) -> Arc { @@ -2295,6 +2524,7 @@ impl Session { sandbox_policy: turn_context.sandbox_policy.clone(), codex_linux_sandbox_exe: turn_context.codex_linux_sandbox_exe.clone(), sandbox_cwd: turn_context.cwd.clone(), + use_linux_sandbox_bwrap: turn_context.features.enabled(Feature::UseLinuxSandboxBwrap), }; let cancel_token = self.reset_mcp_startup_cancellation_token().await; @@ -2531,6 +2761,7 @@ async fn submission_loop(sess: Arc, config: Arc, rx_sub: Receiv mod handlers { use crate::codex::Session; use crate::codex::SessionSettingsUpdate; + use crate::codex::SteerInputError; use crate::codex::TurnContext; use crate::codex::spawn_review_thread; @@ -2544,7 +2775,9 @@ mod handlers { use crate::tasks::CompactTask; use crate::tasks::RegularTask; use crate::tasks::UndoTask; + use crate::tasks::UserShellCommandMode; use crate::tasks::UserShellCommandTask; + use crate::tasks::execute_user_shell_command; use codex_protocol::custom_prompts::CustomPrompt; use codex_protocol::protocol::CodexErrorInfo; use codex_protocol::protocol::ErrorEvent; @@ -2661,16 +2894,17 @@ mod handlers { // new_turn_with_sub_id already emits the error event. return; }; - current_context - .client - .get_otel_manager() - .user_prompt(&items); + current_context.otel_manager.user_prompt(&items); - // Attempt to inject input into current task - if let Err(items) = sess.inject_input(items).await { + // Attempt to inject input into current task. + if let Err(SteerInputError::NoActiveTurn(items)) = sess.steer_input(items, None).await { sess.seed_initial_context_if_needed(¤t_context).await; - let update_items = - sess.build_settings_update_items(previous_context.as_ref(), ¤t_context); + let resumed_model = sess.take_pending_resume_previous_model().await; + let update_items = sess.build_settings_update_items( + previous_context.as_ref(), + resumed_model.as_deref(), + ¤t_context, + ); if !update_items.is_empty() { sess.record_conversation_items(¤t_context, &update_items) .await; @@ -2690,6 +2924,23 @@ mod handlers { command: String, previous_context: &mut Option>, ) { + if let Some((turn_context, cancellation_token)) = + sess.active_turn_context_and_cancellation_token().await + { + let session = Arc::clone(sess); + tokio::spawn(async move { + execute_user_shell_command( + session, + turn_context, + command, + cancellation_token, + UserShellCommandMode::ActiveTurnAuxiliary, + ) + .await; + }); + return; + } + let turn_context = sess.new_default_turn_with_sub_id(sub_id).await; sess.spawn_task( Arc::clone(&turn_context), @@ -3206,7 +3457,7 @@ async fn spawn_review_thread( let model = config .review_model .clone() - .unwrap_or_else(|| parent_turn_context.client.get_model()); + .unwrap_or_else(|| parent_turn_context.model_info.slug.clone()); let review_model_info = sess .services .models_manager @@ -3225,8 +3476,8 @@ async fn spawn_review_thread( }); let review_prompt = resolved.prompt.clone(); - let provider = parent_turn_context.client.get_provider(); - let auth_manager = parent_turn_context.client.get_auth_manager(); + let provider = parent_turn_context.provider.clone(); + let auth_manager = parent_turn_context.auth_manager.clone(); let model_info = review_model_info.clone(); // Build per‑turn client with the requested model/family. @@ -3236,28 +3487,30 @@ async fn spawn_review_thread( per_turn_config.web_search_mode = Some(review_web_search_mode); let otel_manager = parent_turn_context - .client - .get_otel_manager() + .otel_manager + .clone() .with_model(model.as_str(), review_model_info.slug.as_str()); + let auth_manager_for_context = auth_manager.clone(); + let provider_for_context = provider.clone(); + let otel_manager_for_context = otel_manager.clone(); + let reasoning_effort = per_turn_config.model_reasoning_effort; + let reasoning_summary = per_turn_config.model_reasoning_summary; + let session_source = parent_turn_context.session_source.clone(); let per_turn_config = Arc::new(per_turn_config); - let client = ModelClient::new( - per_turn_config.clone(), - auth_manager, - model_info.clone(), - otel_manager, - provider, - per_turn_config.model_reasoning_effort, - per_turn_config.model_reasoning_summary, - sess.conversation_id, - parent_turn_context.client.get_session_source(), - parent_turn_context.client.transport_manager(), - ); let review_turn_context = TurnContext { sub_id: sub_id.to_string(), - client, + config: per_turn_config, + auth_manager: auth_manager_for_context, + model_info: model_info.clone(), + otel_manager: otel_manager_for_context, + provider: provider_for_context, + reasoning_effort, + reasoning_summary, + session_source, tools_config, + features: parent_turn_context.features.clone(), ghost_snapshot: parent_turn_context.ghost_snapshot.clone(), developer_instructions: None, user_instructions: None, @@ -3274,6 +3527,7 @@ async fn spawn_review_thread( tool_call_gate: Arc::new(ReadinessFlag::new()), dynamic_tools: parent_turn_context.dynamic_tools.clone(), truncation_policy: model_info.truncation_policy.into(), + turn_metadata_header: parent_turn_context.turn_metadata_header.clone(), }; // Seed the child task with the review prompt as the initial user message. @@ -3372,12 +3626,12 @@ pub(crate) async fn run_turn( return None; } - let model_info = turn_context.client.get_model_info(); + let model_info = turn_context.model_info.clone(); let auto_compact_limit = model_info.auto_compact_token_limit().unwrap_or(i64::MAX); let total_usage_tokens = sess.get_total_token_usage().await; let event = EventMsg::TurnStarted(TurnStartedEvent { - model_context_window: turn_context.client.get_model_context_window(), + model_context_window: turn_context.model_context_window(), collaboration_mode_kind: turn_context.collaboration_mode.mode, }); sess.send_event(&turn_context, event).await; @@ -3396,7 +3650,7 @@ pub(crate) async fn run_turn( || (HashMap::new(), HashMap::new()), |outcome| build_skill_name_counts(&outcome.skills, &outcome.disabled_paths), ); - let connector_slug_counts = if turn_context.client.config().features.enabled(Feature::Apps) { + let connector_slug_counts = if turn_context.config.features.enabled(Feature::Apps) { let mcp_tools = match sess .services .mcp_connection_manager @@ -3425,7 +3679,7 @@ pub(crate) async fn run_turn( }); let explicit_app_paths = collect_explicit_app_paths(&input); - let config = turn_context.client.config(); + let config = turn_context.config.clone(); if config .features .enabled(Feature::SkillEnvVarDependencyPrompt) @@ -3442,9 +3696,9 @@ pub(crate) async fn run_turn( ) .await; - let otel_manager = turn_context.client.get_otel_manager(); + let otel_manager = turn_context.otel_manager.clone(); let thread_id = sess.conversation_id.to_string(); - let tracking = build_track_events_context(turn_context.client.get_model(), thread_id); + let tracking = build_track_events_context(turn_context.model_info.slug.clone(), thread_id); let SkillInjections { items: skill_items, warnings: skill_warnings, @@ -3478,27 +3732,44 @@ pub(crate) async fn run_turn( // many turns, from the perspective of the user, it is a single turn. let turn_diff_tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::new())); - let mut client_session = turn_context - .client - .new_session(Some(turn_context.cwd.clone())); + let turn_metadata_header = turn_context.resolve_turn_metadata_header().await; + // `ModelClientSession` is turn-scoped and caches WebSocket + sticky routing state, so we reuse + // one instance across retries within this turn. + let mut client_session = sess.services.model_client.new_session(); loop { // Note that pending_input would be something like a message the user // submitted through the UI while the model was running. Though the UI // may support this, the model might not. - let pending_input = sess + let pending_response_items = sess .get_pending_input() .await .into_iter() .map(ResponseItem::from) .collect::>(); + if !pending_response_items.is_empty() { + for response_item in pending_response_items { + if let Some(TurnItem::UserMessage(user_message)) = parse_turn_item(&response_item) { + // todo(aibrahim): move pending input to be UserInput only to keep TextElements. context: https://github.com/openai/codex/pull/10656#discussion_r2765522480 + sess.record_user_prompt_and_emit_turn_item( + turn_context.as_ref(), + &user_message.content, + response_item, + ) + .await; + } else { + sess.record_conversation_items( + &turn_context, + std::slice::from_ref(&response_item), + ) + .await; + } + } + } + // Construct the input that we will send to the model. - let sampling_request_input: Vec = { - sess.record_conversation_items(&turn_context, &pending_input) - .await; - sess.clone_history().await.for_prompt() - }; + let sampling_request_input: Vec = { sess.clone_history().await.for_prompt() }; let sampling_request_input_messages = sampling_request_input .iter() @@ -3517,6 +3788,7 @@ pub(crate) async fn run_turn( Arc::clone(&turn_context), Arc::clone(&turn_diff_tracker), &mut client_session, + turn_metadata_header.as_deref(), sampling_request_input, tool_selection, cancellation_token.child_token(), @@ -3552,14 +3824,21 @@ pub(crate) async fn run_turn( if !needs_follow_up { last_agent_message = sampling_request_last_agent_message; - sess.notifier() - .notify(&UserNotification::AgentTurnComplete { - thread_id: sess.conversation_id.to_string(), - turn_id: turn_context.sub_id.clone(), - cwd: turn_context.cwd.display().to_string(), - input_messages: sampling_request_input_messages, - last_assistant_message: last_agent_message.clone(), - }); + sess.hooks() + .dispatch(crate::hooks::HookPayload { + session_id: sess.conversation_id, + cwd: turn_context.cwd.clone(), + triggered_at: chrono::Utc::now(), + hook_event: HookEvent::AfterAgent { + event: HookEventAfterAgent { + thread_id: sess.conversation_id, + turn_id: turn_context.sub_id.clone(), + input_messages: sampling_request_input_messages, + last_assistant_message: last_agent_message.clone(), + }, + }, + }) + .await; break; } continue; @@ -3598,7 +3877,7 @@ pub(crate) async fn run_turn( } async fn run_auto_compact(sess: &Arc, turn_context: &Arc) { - if should_use_remote_compact_task(sess.as_ref(), &turn_context.client.get_provider()) { + if should_use_remote_compact_task(sess.as_ref(), &turn_context.provider) { run_inline_remote_auto_compact_task(Arc::clone(sess), Arc::clone(turn_context)).await; } else { run_inline_auto_compact_task(Arc::clone(sess), Arc::clone(turn_context)).await; @@ -3703,11 +3982,12 @@ struct SamplingRequestToolSelection<'a> { skill_name_counts_lower: &'a HashMap, } +#[allow(clippy::too_many_arguments)] #[instrument(level = "trace", skip_all, fields( turn_id = %turn_context.sub_id, - model = %turn_context.client.get_model(), + model = %turn_context.model_info.slug, cwd = %turn_context.cwd.display() ) )] @@ -3716,6 +3996,7 @@ async fn run_sampling_request( turn_context: Arc, turn_diff_tracker: SharedTurnDiffTracker, client_session: &mut ModelClientSession, + turn_metadata_header: Option<&str>, input: Vec, tool_selection: SamplingRequestToolSelection<'_>, cancellation_token: CancellationToken, @@ -3728,7 +4009,7 @@ async fn run_sampling_request( .list_all_tools() .or_cancel(&cancellation_token) .await?; - let connectors_for_tools = if turn_context.client.config().features.enabled(Feature::Apps) { + let connectors_for_tools = if turn_context.config.features.enabled(Feature::Apps) { let connectors = connectors::accessible_connectors_from_mcp_tools(&mcp_tools); Some(filter_connectors_for_input( connectors, @@ -3753,10 +4034,7 @@ async fn run_sampling_request( turn_context.dynamic_tools.as_slice(), )); - let model_supports_parallel = turn_context - .client - .get_model_info() - .supports_parallel_tool_calls; + let model_supports_parallel = turn_context.model_info.supports_parallel_tool_calls; let base_instructions = sess.get_base_instructions().await; @@ -3776,6 +4054,7 @@ async fn run_sampling_request( Arc::clone(&sess), Arc::clone(&turn_context), client_session, + turn_metadata_header, Arc::clone(&turn_diff_tracker), &prompt, cancellation_token.child_token(), @@ -3804,8 +4083,10 @@ async fn run_sampling_request( } // Use the configured provider-specific stream retry budget. - let max_retries = turn_context.client.get_provider().stream_max_retries(); - if retries >= max_retries && client_session.try_switch_fallback_transport() { + let max_retries = turn_context.provider.stream_max_retries(); + if retries >= max_retries + && client_session.try_switch_fallback_transport(&turn_context.otel_manager) + { sess.send_event( &turn_context, EventMsg::Warning(WarningEvent { @@ -4250,7 +4531,7 @@ async fn drain_in_flight( skip_all, fields( turn_id = %turn_context.sub_id, - model = %turn_context.client.get_model() + model = %turn_context.model_info.slug ) )] async fn try_run_sampling_request( @@ -4258,6 +4539,7 @@ async fn try_run_sampling_request( sess: Arc, turn_context: Arc, client_session: &mut ModelClientSession, + turn_metadata_header: Option<&str>, turn_diff_tracker: SharedTurnDiffTracker, prompt: &Prompt, cancellation_token: CancellationToken, @@ -4267,11 +4549,11 @@ async fn try_run_sampling_request( cwd: turn_context.cwd.clone(), approval_policy: turn_context.approval_policy, sandbox_policy: turn_context.sandbox_policy.clone(), - model: turn_context.client.get_model(), + model: turn_context.model_info.slug.clone(), personality: turn_context.personality, collaboration_mode: Some(collaboration_mode), - effort: turn_context.client.get_reasoning_effort(), - summary: turn_context.client.get_reasoning_summary(), + effort: turn_context.reasoning_effort, + summary: turn_context.reasoning_summary, user_instructions: turn_context.user_instructions.clone(), developer_instructions: turn_context.developer_instructions.clone(), final_output_json_schema: turn_context.final_output_json_schema.clone(), @@ -4279,17 +4561,24 @@ async fn try_run_sampling_request( }); feedback_tags!( - model = turn_context.client.get_model(), + model = turn_context.model_info.slug.clone(), approval_policy = turn_context.approval_policy, sandbox_policy = turn_context.sandbox_policy, - effort = turn_context.client.get_reasoning_effort(), - auth_mode = sess.services.auth_manager.get_auth_mode(), + effort = turn_context.reasoning_effort, + auth_mode = sess.services.auth_manager.auth_mode(), features = sess.features.enabled_features(), ); sess.persist_rollout_items(&[rollout_item]).await; let mut stream = client_session - .stream(prompt) + .stream( + prompt, + &turn_context.model_info, + &turn_context.otel_manager, + turn_context.reasoning_effort, + turn_context.reasoning_summary, + turn_metadata_header, + ) .instrument(trace_span!("stream_request")) .or_cancel(&cancellation_token) .await??; @@ -4571,6 +4860,7 @@ mod tests { use crate::tools::format_exec_output_str; use codex_protocol::ThreadId; + use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::FunctionCallOutputPayload; use crate::protocol::CompactedItem; @@ -4594,8 +4884,10 @@ mod tests { use crate::tools::registry::ToolHandler; use crate::turn_diff_tracker::TurnDiffTracker; use codex_app_server_protocol::AppInfo; - use codex_app_server_protocol::AuthMode; + use codex_otel::TelemetryAuthMode; + use codex_protocol::models::BaseInstructions; use codex_protocol::models::ContentItem; + use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ResponseItem; use std::path::Path; use std::time::Duration; @@ -4874,6 +5166,46 @@ mod tests { assert_eq!(actual, Some(info2)); } + #[tokio::test] + async fn recompute_token_usage_uses_session_base_instructions() { + let (session, turn_context) = make_session_and_context().await; + + let override_instructions = "SESSION_OVERRIDE_INSTRUCTIONS_ONLY".repeat(120); + { + let mut state = session.state.lock().await; + state.session_configuration.base_instructions = override_instructions.clone(); + } + + let item = user_message("hello"); + session + .record_into_history(std::slice::from_ref(&item), &turn_context) + .await; + + let history = session.clone_history().await; + let session_base_instructions = BaseInstructions { + text: override_instructions, + }; + let expected_tokens = history + .estimate_token_count_with_base_instructions(&session_base_instructions) + .expect("estimate with session base instructions"); + let model_estimated_tokens = history + .estimate_token_count(&turn_context) + .expect("estimate with model instructions"); + assert_ne!(expected_tokens, model_estimated_tokens); + + session.recompute_token_usage(&turn_context).await; + + let actual_tokens = session + .state + .lock() + .await + .token_info() + .expect("token info") + .last_token_usage + .total_tokens; + assert_eq!(actual_tokens, expected_tokens.max(0)); + } + #[tokio::test] async fn record_initial_history_reconstructs_forked_transcript() { let (session, turn_context) = make_session_and_context().await; @@ -5204,13 +5536,14 @@ mod tests { let got = FunctionCallOutputPayload::from(&ctr); let expected = FunctionCallOutputPayload { - content: serde_json::to_string(&json!({ - "ok": true, - "value": 42 - })) - .unwrap(), + body: FunctionCallOutputBody::Text( + serde_json::to_string(&json!({ + "ok": true, + "value": 42 + })) + .unwrap(), + ), success: Some(true), - ..Default::default() }; assert_eq!(expected, got); @@ -5247,10 +5580,10 @@ mod tests { let got = FunctionCallOutputPayload::from(&ctr); let expected = FunctionCallOutputPayload { - content: serde_json::to_string(&vec![text_block("hello"), text_block("world")]) - .unwrap(), + body: FunctionCallOutputBody::Text( + serde_json::to_string(&vec![text_block("hello"), text_block("world")]).unwrap(), + ), success: Some(true), - ..Default::default() }; assert_eq!(expected, got); @@ -5267,9 +5600,10 @@ mod tests { let got = FunctionCallOutputPayload::from(&ctr); let expected = FunctionCallOutputPayload { - content: serde_json::to_string(&json!({ "message": "bad" })).unwrap(), + body: FunctionCallOutputBody::Text( + serde_json::to_string(&json!({ "message": "bad" })).unwrap(), + ), success: Some(false), - ..Default::default() }; assert_eq!(expected, got); @@ -5286,9 +5620,10 @@ mod tests { let got = FunctionCallOutputPayload::from(&ctr); let expected = FunctionCallOutputPayload { - content: serde_json::to_string(&vec![text_block("alpha")]).unwrap(), + body: FunctionCallOutputBody::Text( + serde_json::to_string(&vec![text_block("alpha")]).unwrap(), + ), success: Some(true), - ..Default::default() }; assert_eq!(expected, got); @@ -5359,7 +5694,7 @@ mod tests { model_info.slug.as_str(), None, Some("test@test.com".to_string()), - Some(AuthMode::Chatgpt), + Some(TelemetryAuthMode::Chatgpt), false, "test".to_string(), session_source, @@ -5430,6 +5765,7 @@ mod tests { mark_state_initial_context_seeded(&mut state); let skills_manager = Arc::new(SkillsManager::new(config.codex_home.clone())); + let file_watcher = Arc::new(FileWatcher::noop()); let services = SessionServices { mcp_connection_manager: Arc::new(RwLock::new(McpConnectionManager::default())), mcp_startup_cancellation_token: Mutex::new(CancellationToken::new()), @@ -5438,7 +5774,7 @@ mod tests { Arc::clone(&config), Arc::clone(&auth_manager), ), - notifier: UserNotifier::new(None), + hooks: Hooks::new(&config), rollout: Mutex::new(None), user_shell: Arc::new(default_user_shell()), show_raw_agent_reasoning: config.show_raw_agent_reasoning, @@ -5448,9 +5784,20 @@ mod tests { models_manager: Arc::clone(&models_manager), tool_approvals: Mutex::new(ApprovalStore::default()), skills_manager, + file_watcher, agent_control, state_db: None, - transport_manager: TransportManager::new(), + model_client: ModelClient::new( + Some(auth_manager.clone()), + conversation_id, + session_configuration.provider.clone(), + session_configuration.session_source.clone(), + config.model_verbosity, + config.features.enabled(Feature::ResponsesWebsockets), + config.features.enabled(Feature::EnableRequestCompression), + config.features.enabled(Feature::RuntimeMetrics), + Session::build_model_client_beta_features_header(config.as_ref()), + ), }; let turn_context = Session::make_turn_context( @@ -5460,9 +5807,7 @@ mod tests { &session_configuration, per_turn_config, model_info, - conversation_id, "turn_id".to_string(), - services.transport_manager.clone(), ); let session = Session { @@ -5550,6 +5895,7 @@ mod tests { mark_state_initial_context_seeded(&mut state); let skills_manager = Arc::new(SkillsManager::new(config.codex_home.clone())); + let file_watcher = Arc::new(FileWatcher::noop()); let services = SessionServices { mcp_connection_manager: Arc::new(RwLock::new(McpConnectionManager::default())), mcp_startup_cancellation_token: Mutex::new(CancellationToken::new()), @@ -5558,7 +5904,7 @@ mod tests { Arc::clone(&config), Arc::clone(&auth_manager), ), - notifier: UserNotifier::new(None), + hooks: Hooks::new(&config), rollout: Mutex::new(None), user_shell: Arc::new(default_user_shell()), show_raw_agent_reasoning: config.show_raw_agent_reasoning, @@ -5568,9 +5914,20 @@ mod tests { models_manager: Arc::clone(&models_manager), tool_approvals: Mutex::new(ApprovalStore::default()), skills_manager, + file_watcher, agent_control, state_db: None, - transport_manager: TransportManager::new(), + model_client: ModelClient::new( + Some(Arc::clone(&auth_manager)), + conversation_id, + session_configuration.provider.clone(), + session_configuration.session_source.clone(), + config.model_verbosity, + config.features.enabled(Feature::ResponsesWebsockets), + config.features.enabled(Feature::EnableRequestCompression), + config.features.enabled(Feature::RuntimeMetrics), + Session::build_model_client_beta_features_header(config.as_ref()), + ), }; let turn_context = Arc::new(Session::make_turn_context( @@ -5580,9 +5937,7 @@ mod tests { &session_configuration, per_turn_config, model_info, - conversation_id, "turn_id".to_string(), - services.transport_manager.clone(), )); let session = Arc::new(Session { @@ -5738,7 +6093,7 @@ mod tests { } #[tokio::test] - async fn abort_gracefuly_emits_turn_aborted_only() { + async fn abort_gracefully_emits_turn_aborted_only() { let (sess, tc, rx) = make_session_and_context_with_rx().await; let input = vec![UserInput::Text { text: "hello".to_string(), @@ -5770,6 +6125,133 @@ mod tests { assert!(rx.try_recv().is_err()); } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn task_finish_persists_leftover_pending_input() { + let (sess, tc, _rx) = make_session_and_context_with_rx().await; + let input = vec![UserInput::Text { + text: "hello".to_string(), + text_elements: Vec::new(), + }]; + sess.spawn_task( + Arc::clone(&tc), + input, + NeverEndingTask { + kind: TaskKind::Regular, + listen_to_cancellation_token: false, + }, + ) + .await; + + sess.inject_response_items(vec![ResponseInputItem::Message { + role: "user".to_string(), + content: vec![ContentItem::InputText { + text: "late pending input".to_string(), + }], + }]) + .await + .expect("inject pending input into active turn"); + + sess.on_task_finished(Arc::clone(&tc), None).await; + + let history = sess.clone_history().await; + let expected = ResponseItem::Message { + id: None, + role: "user".to_string(), + content: vec![ContentItem::InputText { + text: "late pending input".to_string(), + }], + end_turn: None, + phase: None, + }; + assert!( + history.raw_items().iter().any(|item| item == &expected), + "expected pending input to be persisted into history on turn completion" + ); + } + + #[tokio::test] + async fn steer_input_requires_active_turn() { + let (sess, _tc, _rx) = make_session_and_context_with_rx().await; + let input = vec![UserInput::Text { + text: "steer".to_string(), + text_elements: Vec::new(), + }]; + + let err = sess + .steer_input(input, None) + .await + .expect_err("steering without active turn should fail"); + + assert!(matches!(err, SteerInputError::NoActiveTurn(_))); + } + + #[tokio::test] + async fn steer_input_enforces_expected_turn_id() { + let (sess, tc, _rx) = make_session_and_context_with_rx().await; + let input = vec![UserInput::Text { + text: "hello".to_string(), + text_elements: Vec::new(), + }]; + sess.spawn_task( + Arc::clone(&tc), + input, + NeverEndingTask { + kind: TaskKind::Regular, + listen_to_cancellation_token: false, + }, + ) + .await; + + let steer_input = vec![UserInput::Text { + text: "steer".to_string(), + text_elements: Vec::new(), + }]; + let err = sess + .steer_input(steer_input, Some("different-turn-id")) + .await + .expect_err("mismatched expected turn id should fail"); + + match err { + SteerInputError::ExpectedTurnMismatch { expected, actual } => { + assert_eq!( + (expected, actual), + ("different-turn-id".to_string(), tc.sub_id.clone()) + ); + } + other => panic!("unexpected error: {other:?}"), + } + } + + #[tokio::test] + async fn steer_input_returns_active_turn_id() { + let (sess, tc, _rx) = make_session_and_context_with_rx().await; + let input = vec![UserInput::Text { + text: "hello".to_string(), + text_elements: Vec::new(), + }]; + sess.spawn_task( + Arc::clone(&tc), + input, + NeverEndingTask { + kind: TaskKind::Regular, + listen_to_cancellation_token: false, + }, + ) + .await; + + let steer_input = vec![UserInput::Text { + text: "steer".to_string(), + text_elements: Vec::new(), + }]; + let turn_id = sess + .steer_input(steer_input, Some(&tc.sub_id)) + .await + .expect("steering with matching expected turn id should succeed"); + + assert_eq!(turn_id, tc.sub_id); + assert!(sess.has_pending_input().await); + } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn abort_review_task_emits_exited_then_aborted_and_records_history() { let (sess, tc, rx) = make_session_and_context_with_rx().await; @@ -6129,7 +6611,10 @@ mod tests { .await; let output = match resp2.expect("expected Ok result") { - ToolOutput::Function { content, .. } => content, + ToolOutput::Function { + body: FunctionCallOutputBody::Text(content), + .. + } => content, _ => panic!("unexpected tool output"), }; diff --git a/codex-rs/core/src/codex_delegate.rs b/codex-rs/core/src/codex_delegate.rs index 6499370fc3..cb5a31a0cc 100644 --- a/codex-rs/core/src/codex_delegate.rs +++ b/codex-rs/core/src/codex_delegate.rs @@ -54,6 +54,7 @@ pub(crate) async fn run_codex_thread_interactive( auth_manager, models_manager, Arc::clone(&parent_session.services.skills_manager), + Arc::clone(&parent_session.services.file_watcher), initial_history.unwrap_or(InitialHistory::New), SessionSource::SubAgent(SubAgentSource::Review), parent_session.services.agent_control.clone(), diff --git a/codex-rs/core/src/codex_thread.rs b/codex-rs/core/src/codex_thread.rs index fb8e466d71..0c0bbe0e0d 100644 --- a/codex-rs/core/src/codex_thread.rs +++ b/codex-rs/core/src/codex_thread.rs @@ -1,5 +1,6 @@ use crate::agent::AgentStatus; use crate::codex::Codex; +use crate::codex::SteerInputError; use crate::error::Result as CodexResult; use crate::protocol::Event; use crate::protocol::Op; @@ -9,6 +10,7 @@ use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::SandboxPolicy; use codex_protocol::protocol::SessionSource; +use codex_protocol::user_input::UserInput; use std::path::PathBuf; use tokio::sync::watch; @@ -45,6 +47,14 @@ impl CodexThread { self.codex.submit(op).await } + pub async fn steer_input( + &self, + input: Vec, + expected_turn_id: Option<&str>, + ) -> Result { + self.codex.steer_input(input, expected_turn_id).await + } + /// Use sparingly: this is intended to be removed soon. pub async fn submit_with_id(&self, sub: Submission) -> CodexResult<()> { self.codex.submit_with_id(sub).await diff --git a/codex-rs/core/src/compact.rs b/codex-rs/core/src/compact.rs index ee94e4994b..ab9a9f15d2 100644 --- a/codex-rs/core/src/compact.rs +++ b/codex-rs/core/src/compact.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use crate::ModelProviderInfo; use crate::Prompt; +use crate::client::ModelClientSession; use crate::client_common::ResponseEvent; use crate::codex::Session; use crate::codex::TurnContext; @@ -60,7 +61,7 @@ pub(crate) async fn run_compact_task( input: Vec, ) { let start_event = EventMsg::TurnStarted(TurnStartedEvent { - model_context_window: turn_context.client.get_model_context_window(), + model_context_window: turn_context.model_context_window(), collaboration_mode_kind: turn_context.collaboration_mode.mode, }); sess.send_event(&turn_context, start_event).await; @@ -85,8 +86,12 @@ async fn run_compact_task_inner( let mut truncated_count = 0usize; - let max_retries = turn_context.client.get_provider().stream_max_retries(); + let max_retries = turn_context.provider.stream_max_retries(); let mut retries = 0; + let turn_metadata_header = turn_context.resolve_turn_metadata_header().await; + let mut client_session = sess.services.model_client.new_session(); + // Reuse one client session so turn-scoped state (sticky routing, websocket append tracking) + // survives retries within this compact turn. // TODO: If we need to guarantee the persisted mode always matches the prompt used for this // turn, capture it in TurnContext at creation time. Using SessionConfiguration here avoids @@ -97,11 +102,11 @@ async fn run_compact_task_inner( cwd: turn_context.cwd.clone(), approval_policy: turn_context.approval_policy, sandbox_policy: turn_context.sandbox_policy.clone(), - model: turn_context.client.get_model(), + model: turn_context.model_info.slug.clone(), personality: turn_context.personality, collaboration_mode: Some(collaboration_mode), - effort: turn_context.client.get_reasoning_effort(), - summary: turn_context.client.get_reasoning_summary(), + effort: turn_context.reasoning_effort, + summary: turn_context.reasoning_summary, user_instructions: turn_context.user_instructions.clone(), developer_instructions: turn_context.developer_instructions.clone(), final_output_json_schema: turn_context.final_output_json_schema.clone(), @@ -119,7 +124,14 @@ async fn run_compact_task_inner( personality: turn_context.personality, ..Default::default() }; - let attempt_result = drain_to_completed(&sess, turn_context.as_ref(), &prompt).await; + let attempt_result = drain_to_completed( + &sess, + turn_context.as_ref(), + &mut client_session, + turn_metadata_header.as_deref(), + &prompt, + ) + .await; match attempt_result { Ok(()) => { @@ -335,12 +347,20 @@ fn build_compacted_history_with_limit( async fn drain_to_completed( sess: &Session, turn_context: &TurnContext, + client_session: &mut ModelClientSession, + turn_metadata_header: Option<&str>, prompt: &Prompt, ) -> CodexResult<()> { - let mut client_session = turn_context - .client - .new_session(Some(turn_context.cwd.clone())); - let mut stream = client_session.stream(prompt).await?; + let mut stream = client_session + .stream( + prompt, + &turn_context.model_info, + &turn_context.otel_manager, + turn_context.reasoning_effort, + turn_context.reasoning_summary, + turn_metadata_header, + ) + .await?; loop { let maybe_event = stream.next().await; let Some(event) = maybe_event else { diff --git a/codex-rs/core/src/compact_remote.rs b/codex-rs/core/src/compact_remote.rs index 12bc769ce2..f627bcb473 100644 --- a/codex-rs/core/src/compact_remote.rs +++ b/codex-rs/core/src/compact_remote.rs @@ -12,6 +12,7 @@ use crate::protocol::RolloutItem; use crate::protocol::TurnStartedEvent; use codex_protocol::items::ContextCompactionItem; use codex_protocol::items::TurnItem; +use codex_protocol::models::BaseInstructions; use codex_protocol::models::ResponseItem; use tracing::info; @@ -24,7 +25,7 @@ pub(crate) async fn run_inline_remote_auto_compact_task( pub(crate) async fn run_remote_compact_task(sess: Arc, turn_context: Arc) { let start_event = EventMsg::TurnStarted(TurnStartedEvent { - model_context_window: turn_context.client.get_model_context_window(), + model_context_window: turn_context.model_context_window(), collaboration_mode_kind: turn_context.collaboration_mode.mode, }); sess.send_event(&turn_context, start_event).await; @@ -49,8 +50,12 @@ async fn run_remote_compact_task_inner_impl( sess.emit_turn_item_started(turn_context, &compaction_item) .await; let mut history = sess.clone_history().await; - let deleted_items = - trim_function_call_history_to_fit_context_window(&mut history, turn_context.as_ref()); + let base_instructions = sess.get_base_instructions().await; + let deleted_items = trim_function_call_history_to_fit_context_window( + &mut history, + turn_context.as_ref(), + &base_instructions, + ); if deleted_items > 0 { info!( turn_id = %turn_context.sub_id, @@ -71,14 +76,19 @@ async fn run_remote_compact_task_inner_impl( input: history.for_prompt(), tools: vec![], parallel_tool_calls: false, - base_instructions: sess.get_base_instructions().await, + base_instructions, personality: turn_context.personality, output_schema: None, }; - let mut new_history = turn_context - .client - .compact_conversation_history(&prompt) + let mut new_history = sess + .services + .model_client + .compact_conversation_history( + &prompt, + &turn_context.model_info, + &turn_context.otel_manager, + ) .await?; if !ghost_snapshots.is_empty() { @@ -102,14 +112,15 @@ async fn run_remote_compact_task_inner_impl( fn trim_function_call_history_to_fit_context_window( history: &mut ContextManager, turn_context: &TurnContext, + base_instructions: &BaseInstructions, ) -> usize { let mut deleted_items = 0usize; - let Some(context_window) = turn_context.client.get_model_context_window() else { + let Some(context_window) = turn_context.model_context_window() else { return deleted_items; }; while history - .estimate_token_count(turn_context) + .estimate_token_count_with_base_instructions(base_instructions) .is_some_and(|estimated_tokens| estimated_tokens > context_window) { let Some(last_item) = history.raw_items().last() else { diff --git a/codex-rs/core/src/config/edit.rs b/codex-rs/core/src/config/edit.rs index cf87451821..94cd726711 100644 --- a/codex-rs/core/src/config/edit.rs +++ b/codex-rs/core/src/config/edit.rs @@ -55,6 +55,24 @@ pub enum ConfigEdit { ClearPath { segments: Vec }, } +pub fn status_line_items_edit(items: &[String]) -> ConfigEdit { + if items.is_empty() { + return ConfigEdit::ClearPath { + segments: vec!["tui".to_string(), "status_line".to_string()], + }; + } + + let mut array = toml_edit::Array::new(); + for item in items { + array.push(item.clone()); + } + + ConfigEdit::SetPath { + segments: vec!["tui".to_string(), "status_line".to_string()], + value: TomlItem::Value(array.into()), + } +} + // TODO(jif) move to a dedicated file mod document_helpers { use crate::config::types::McpServerConfig; diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 949c08724f..7de6f00ea8 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -21,6 +21,7 @@ use crate::config::types::UriBasedFileOpener; use crate::config_loader::CloudRequirementsLoader; use crate::config_loader::ConfigLayerStack; use crate::config_loader::ConfigRequirements; +use crate::config_loader::ConstrainedWithSource; use crate::config_loader::LoaderOverrides; use crate::config_loader::McpServerIdentity; use crate::config_loader::McpServerRequirement; @@ -116,6 +117,9 @@ pub struct Config { /// requirements). pub config_layer_stack: ConfigLayerStack, + /// Warnings collected during config load that should be shown on startup. + pub startup_warnings: Vec, + /// Optional override of model selection. pub model: Option, @@ -224,6 +228,9 @@ pub struct Config { /// - `never`: Never use alternate screen (inline mode, preserves scrollback). pub tui_alternate_screen: AltScreenMode, + /// Ordered list of status line item identifiers for the TUI. + pub tui_status_line: Option>, + /// The directory that should be treated as the current working directory /// for the session. All relative paths inside the business-logic layer are /// resolved against this path. @@ -271,6 +278,9 @@ pub struct Config { /// overridden by the `CODEX_HOME` environment variable). pub codex_home: PathBuf, + /// Directory where Codex writes log files (defaults to `$CODEX_HOME/log`). + pub log_dir: PathBuf, + /// Settings that govern if and what will be written to `~/.codex/history.jsonl`. pub history: History, @@ -595,6 +605,41 @@ fn constrain_mcp_servers( }) } +fn apply_requirement_constrained_value( + field_name: &'static str, + configured_value: T, + constrained_value: &mut ConstrainedWithSource, + startup_warnings: &mut Vec, +) -> std::io::Result<()> +where + T: Clone + std::fmt::Debug + Send + Sync, +{ + if let Err(err) = constrained_value.set(configured_value) { + let fallback_value = constrained_value.get().clone(); + tracing::warn!( + error = %err, + ?fallback_value, + requirement_source = ?constrained_value.source, + "configured value is disallowed by requirements; falling back to required value for {field_name}" + ); + let message = format!( + "Configured value for `{field_name}` is disallowed by requirements; falling back to required value {fallback_value:?}. Details: {err}" + ); + startup_warnings.push(message); + + constrained_value.set(fallback_value).map_err(|fallback_err| { + std::io::Error::new( + std::io::ErrorKind::InvalidInput, + format!( + "configured value for `{field_name}` is disallowed by requirements ({err}); fallback to a requirement-compliant value also failed ({fallback_err})" + ), + ) + })?; + } + + Ok(()) +} + fn mcp_server_matches_requirement( requirement: &McpServerRequirement, server: &McpServerConfig, @@ -896,6 +941,10 @@ pub struct ConfigToml { #[serde(default)] pub history: Option, + /// Directory where Codex writes log files, for example `codex-tui.log`. + /// Defaults to `$CODEX_HOME/log`. + pub log_dir: Option, + /// Optional URI-based file opener. If set, citations to files in the model /// output will be hyperlinked using the specified URI scheme. pub file_opener: Option, @@ -1278,15 +1327,11 @@ fn resolve_web_search_mode( pub(crate) fn resolve_web_search_mode_for_turn( explicit_mode: Option, - is_azure_responses_endpoint: bool, sandbox_policy: &SandboxPolicy, ) -> WebSearchMode { if let Some(mode) = explicit_mode { return mode; } - if is_azure_responses_endpoint { - return WebSearchMode::Disabled; - } if matches!(sandbox_policy, SandboxPolicy::DangerFullAccess) { WebSearchMode::Live } else { @@ -1314,6 +1359,7 @@ impl Config { ) -> std::io::Result { let requirements = config_layer_stack.requirements().clone(); let user_instructions = Self::load_instructions(Some(&codex_home)); + let mut startup_warnings = Vec::new(); // Destructure ConfigOverrides fully to ensure all overrides are applied. let ConfigOverrides { @@ -1543,7 +1589,7 @@ impl Config { .or_else(|| { features .enabled(Feature::Personality) - .then_some(Personality::Friendly) + .then_some(Personality::Pragmatic) }); let experimental_compact_prompt_path = config_profile @@ -1560,6 +1606,16 @@ impl Config { let check_for_update_on_startup = cfg.check_for_update_on_startup.unwrap_or(true); + let log_dir = cfg + .log_dir + .as_ref() + .map(AbsolutePathBuf::to_path_buf) + .unwrap_or_else(|| { + let mut p = codex_home.clone(); + p.push("log"); + p + }); + // Ensure that every field of ConfigRequirements is applied to the final // Config. let ConfigRequirements { @@ -1570,12 +1626,18 @@ impl Config { enforce_residency, } = requirements; - constrained_approval_policy - .set(approval_policy) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("{e}")))?; - constrained_sandbox_policy - .set(sandbox_policy) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("{e}")))?; + apply_requirement_constrained_value( + "approval_policy", + approval_policy, + &mut constrained_approval_policy, + &mut startup_warnings, + )?; + apply_requirement_constrained_value( + "sandbox_mode", + sandbox_policy, + &mut constrained_sandbox_policy, + &mut startup_warnings, + )?; let mcp_servers = constrain_mcp_servers(cfg.mcp_servers.clone(), mcp_servers.as_ref()) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("{e}")))?; @@ -1588,9 +1650,10 @@ impl Config { model_provider_id, model_provider, cwd: resolved_cwd, - approval_policy: constrained_approval_policy, - sandbox_policy: constrained_sandbox_policy, - enforce_residency, + startup_warnings, + approval_policy: constrained_approval_policy.value, + sandbox_policy: constrained_sandbox_policy.value, + enforce_residency: enforce_residency.value, did_user_set_custom_approval_policy_or_sandbox_mode, forced_auto_mode_downgraded_on_windows, shell_environment_policy, @@ -1626,6 +1689,7 @@ impl Config { tool_output_token_limit: cfg.tool_output_token_limit, agent_max_threads, codex_home, + log_dir, config_layer_stack, history, ephemeral: ephemeral.unwrap_or_default(), @@ -1694,6 +1758,7 @@ impl Config { .as_ref() .map(|t| t.alternate_screen) .unwrap_or_default(), + tui_status_line: cfg.tui.as_ref().and_then(|t| t.status_line.clone()), otel: { let t: OtelConfigToml = cfg.otel.unwrap_or_default(); let log_user_prompt = t.log_user_prompt.unwrap_or(false); @@ -1816,9 +1881,7 @@ pub fn find_codex_home() -> std::io::Result { /// Returns the path to the folder where Codex logs are stored. Does not verify /// that the directory exists. pub fn log_dir(cfg: &Config) -> std::io::Result { - let mut p = cfg.codex_home.clone(); - p.push("log"); - Ok(p) + Ok(cfg.log_dir.clone()) } #[cfg(test)] @@ -1931,6 +1994,7 @@ persistence = "none" show_tooltips: true, experimental_mode: None, alternate_screen: AltScreenMode::Auto, + status_line: None, } ); } @@ -2392,14 +2456,14 @@ trust_level = "trusted" #[test] fn web_search_mode_for_turn_defaults_to_cached_when_unset() { - let mode = resolve_web_search_mode_for_turn(None, false, &SandboxPolicy::ReadOnly); + let mode = resolve_web_search_mode_for_turn(None, &SandboxPolicy::ReadOnly); assert_eq!(mode, WebSearchMode::Cached); } #[test] fn web_search_mode_for_turn_defaults_to_live_for_danger_full_access() { - let mode = resolve_web_search_mode_for_turn(None, false, &SandboxPolicy::DangerFullAccess); + let mode = resolve_web_search_mode_for_turn(None, &SandboxPolicy::DangerFullAccess); assert_eq!(mode, WebSearchMode::Live); } @@ -2408,20 +2472,12 @@ trust_level = "trusted" fn web_search_mode_for_turn_prefers_explicit_value() { let mode = resolve_web_search_mode_for_turn( Some(WebSearchMode::Cached), - false, &SandboxPolicy::DangerFullAccess, ); assert_eq!(mode, WebSearchMode::Cached); } - #[test] - fn web_search_mode_for_turn_disables_for_azure_responses_endpoint() { - let mode = resolve_web_search_mode_for_turn(None, true, &SandboxPolicy::DangerFullAccess); - - assert_eq!(mode, WebSearchMode::Disabled); - } - #[test] fn profile_legacy_toggles_override_base() -> std::io::Result<()> { let codex_home = TempDir::new()?; @@ -3842,7 +3898,9 @@ model_verbosity = "high" tool_output_token_limit: None, agent_max_threads: DEFAULT_AGENT_MAX_THREADS, codex_home: fixture.codex_home(), + log_dir: fixture.codex_home().join("log"), config_layer_stack: Default::default(), + startup_warnings: Vec::new(), history: History::default(), ephemeral: false, file_opener: UriBasedFileOpener::VsCode, @@ -3853,7 +3911,7 @@ model_verbosity = "high" model_reasoning_summary: ReasoningSummary::Detailed, model_supports_reasoning_summaries: None, model_verbosity: None, - personality: Some(Personality::Friendly), + personality: Some(Personality::Pragmatic), chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(), base_instructions: None, developer_instructions: None, @@ -3862,7 +3920,7 @@ model_verbosity = "high" forced_login_method: None, include_apply_patch_tool: false, web_search_mode: None, - use_experimental_unified_exec_tool: false, + use_experimental_unified_exec_tool: !cfg!(windows), ghost_snapshot: GhostSnapshotConfig::default(), features: Features::with_defaults(), suppress_unstable_features_warning: false, @@ -3880,6 +3938,7 @@ model_verbosity = "high" analytics_enabled: Some(true), feedback_enabled: true, tui_alternate_screen: AltScreenMode::Auto, + tui_status_line: None, otel: OtelConfig::default(), }, o3_profile_config @@ -3927,7 +3986,9 @@ model_verbosity = "high" tool_output_token_limit: None, agent_max_threads: DEFAULT_AGENT_MAX_THREADS, codex_home: fixture.codex_home(), + log_dir: fixture.codex_home().join("log"), config_layer_stack: Default::default(), + startup_warnings: Vec::new(), history: History::default(), ephemeral: false, file_opener: UriBasedFileOpener::VsCode, @@ -3938,7 +3999,7 @@ model_verbosity = "high" model_reasoning_summary: ReasoningSummary::default(), model_supports_reasoning_summaries: None, model_verbosity: None, - personality: Some(Personality::Friendly), + personality: Some(Personality::Pragmatic), chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(), base_instructions: None, developer_instructions: None, @@ -3947,7 +4008,7 @@ model_verbosity = "high" forced_login_method: None, include_apply_patch_tool: false, web_search_mode: None, - use_experimental_unified_exec_tool: false, + use_experimental_unified_exec_tool: !cfg!(windows), ghost_snapshot: GhostSnapshotConfig::default(), features: Features::with_defaults(), suppress_unstable_features_warning: false, @@ -3965,6 +4026,7 @@ model_verbosity = "high" analytics_enabled: Some(true), feedback_enabled: true, tui_alternate_screen: AltScreenMode::Auto, + tui_status_line: None, otel: OtelConfig::default(), }; @@ -4027,7 +4089,9 @@ model_verbosity = "high" tool_output_token_limit: None, agent_max_threads: DEFAULT_AGENT_MAX_THREADS, codex_home: fixture.codex_home(), + log_dir: fixture.codex_home().join("log"), config_layer_stack: Default::default(), + startup_warnings: Vec::new(), history: History::default(), ephemeral: false, file_opener: UriBasedFileOpener::VsCode, @@ -4038,7 +4102,7 @@ model_verbosity = "high" model_reasoning_summary: ReasoningSummary::default(), model_supports_reasoning_summaries: None, model_verbosity: None, - personality: Some(Personality::Friendly), + personality: Some(Personality::Pragmatic), chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(), base_instructions: None, developer_instructions: None, @@ -4047,7 +4111,7 @@ model_verbosity = "high" forced_login_method: None, include_apply_patch_tool: false, web_search_mode: None, - use_experimental_unified_exec_tool: false, + use_experimental_unified_exec_tool: !cfg!(windows), ghost_snapshot: GhostSnapshotConfig::default(), features: Features::with_defaults(), suppress_unstable_features_warning: false, @@ -4065,6 +4129,7 @@ model_verbosity = "high" analytics_enabled: Some(false), feedback_enabled: true, tui_alternate_screen: AltScreenMode::Auto, + tui_status_line: None, otel: OtelConfig::default(), }; @@ -4113,7 +4178,9 @@ model_verbosity = "high" tool_output_token_limit: None, agent_max_threads: DEFAULT_AGENT_MAX_THREADS, codex_home: fixture.codex_home(), + log_dir: fixture.codex_home().join("log"), config_layer_stack: Default::default(), + startup_warnings: Vec::new(), history: History::default(), ephemeral: false, file_opener: UriBasedFileOpener::VsCode, @@ -4124,7 +4191,7 @@ model_verbosity = "high" model_reasoning_summary: ReasoningSummary::Detailed, model_supports_reasoning_summaries: None, model_verbosity: Some(Verbosity::High), - personality: Some(Personality::Friendly), + personality: Some(Personality::Pragmatic), chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(), base_instructions: None, developer_instructions: None, @@ -4133,7 +4200,7 @@ model_verbosity = "high" forced_login_method: None, include_apply_patch_tool: false, web_search_mode: None, - use_experimental_unified_exec_tool: false, + use_experimental_unified_exec_tool: !cfg!(windows), ghost_snapshot: GhostSnapshotConfig::default(), features: Features::with_defaults(), suppress_unstable_features_warning: false, @@ -4151,6 +4218,7 @@ model_verbosity = "high" analytics_enabled: Some(true), feedback_enabled: true, tui_alternate_screen: AltScreenMode::Auto, + tui_status_line: None, otel: OtelConfig::default(), }; @@ -4664,7 +4732,7 @@ mcp_oauth_callback_port = 5678 } #[tokio::test] - async fn explicit_sandbox_mode_still_errors_when_disallowed_by_requirements() + async fn explicit_sandbox_mode_falls_back_when_disallowed_by_requirements() -> std::io::Result<()> { let codex_home = TempDir::new()?; std::fs::write( @@ -4683,19 +4751,15 @@ mcp_oauth_callback_port = 5678 enforce_residency: None, }; - let err = ConfigBuilder::default() + let config = ConfigBuilder::default() .codex_home(codex_home.path().to_path_buf()) .fallback_cwd(Some(codex_home.path().to_path_buf())) .cloud_requirements(CloudRequirementsLoader::new( async move { Some(requirements) }, )) .build() - .await - .expect_err("explicit disallowed mode should still fail"); - assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput); - let message = err.to_string(); - assert!(message.contains("invalid value for `sandbox_mode`")); - assert!(message.contains("set by cloud requirements")); + .await?; + assert_eq!(*config.sandbox_policy.get(), SandboxPolicy::ReadOnly); Ok(()) } @@ -4732,7 +4796,7 @@ trust_level = "untrusted" } #[tokio::test] - async fn explicit_approval_policy_still_errors_when_disallowed_by_requirements() + async fn explicit_approval_policy_falls_back_when_disallowed_by_requirements() -> std::io::Result<()> { let codex_home = TempDir::new()?; std::fs::write( @@ -4741,7 +4805,7 @@ trust_level = "untrusted" "#, )?; - let err = ConfigBuilder::default() + let config = ConfigBuilder::default() .codex_home(codex_home.path().to_path_buf()) .fallback_cwd(Some(codex_home.path().to_path_buf())) .cloud_requirements(CloudRequirementsLoader::new(async { @@ -4751,12 +4815,8 @@ trust_level = "untrusted" }) })) .build() - .await - .expect_err("explicit disallowed approval policy should fail"); - assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput); - let message = err.to_string(); - assert!(message.contains("invalid value for `approval_policy`")); - assert!(message.contains("set by cloud requirements")); + .await?; + assert_eq!(config.approval_policy.value(), AskForApproval::OnRequest); Ok(()) } } diff --git a/codex-rs/core/src/config/types.rs b/codex-rs/core/src/config/types.rs index 7ffcf3a8e2..2eb868cc34 100644 --- a/codex-rs/core/src/config/types.rs +++ b/codex-rs/core/src/config/types.rs @@ -486,6 +486,12 @@ pub struct Tui { /// scrollback in terminal multiplexers like Zellij that follow the xterm spec. #[serde(default)] pub alternate_screen: AltScreenMode, + + /// Ordered list of status line item identifiers. + /// + /// When set, the TUI renders the selected items as the status line. + #[serde(default)] + pub status_line: Option>, } const fn default_true() -> bool { diff --git a/codex-rs/core/src/config_loader/config_requirements.rs b/codex-rs/core/src/config_loader/config_requirements.rs index 478c74193e..b3e6043288 100644 --- a/codex-rs/core/src/config_loader/config_requirements.rs +++ b/codex-rs/core/src/config_loader/config_requirements.rs @@ -44,29 +44,67 @@ impl fmt::Display for RequirementSource { } } +#[derive(Debug, Clone, PartialEq)] +pub struct ConstrainedWithSource { + pub value: Constrained, + pub source: Option, +} + +impl ConstrainedWithSource { + pub fn new(value: Constrained, source: Option) -> Self { + Self { value, source } + } +} + +impl std::ops::Deref for ConstrainedWithSource { + type Target = Constrained; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl std::ops::DerefMut for ConstrainedWithSource { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.value + } +} + /// Normalized version of [`ConfigRequirementsToml`] after deserialization and /// normalization. #[derive(Debug, Clone, PartialEq)] pub struct ConfigRequirements { - pub approval_policy: Constrained, - pub sandbox_policy: Constrained, + pub approval_policy: ConstrainedWithSource, + pub sandbox_policy: ConstrainedWithSource, pub mcp_servers: Option>>, pub(crate) exec_policy: Option>, - pub enforce_residency: Constrained>, + pub enforce_residency: ConstrainedWithSource>, } impl Default for ConfigRequirements { fn default() -> Self { Self { - approval_policy: Constrained::allow_any_from_default(), - sandbox_policy: Constrained::allow_any(SandboxPolicy::ReadOnly), + approval_policy: ConstrainedWithSource::new( + Constrained::allow_any_from_default(), + None, + ), + sandbox_policy: ConstrainedWithSource::new( + Constrained::allow_any(SandboxPolicy::ReadOnly), + None, + ), mcp_servers: None, exec_policy: None, - enforce_residency: Constrained::allow_any(None), + enforce_residency: ConstrainedWithSource::new(Constrained::allow_any(None), None), } } } +impl ConfigRequirements { + pub fn exec_policy_source(&self) -> Option<&RequirementSource> { + self.exec_policy.as_ref().map(|policy| &policy.source) + } +} + #[derive(Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(untagged)] pub enum McpServerIdentity { @@ -228,7 +266,7 @@ impl TryFrom for ConfigRequirements { enforce_residency, } = toml; - let approval_policy: Constrained = match allowed_approval_policies { + let approval_policy = match allowed_approval_policies { Some(Sourced { value: policies, source: requirement_source, @@ -237,7 +275,8 @@ impl TryFrom for ConfigRequirements { return Err(ConstraintError::empty_field("allowed_approval_policies")); }; - Constrained::new(initial_value, move |candidate| { + let requirement_source_for_error = requirement_source.clone(); + let constrained = Constrained::new(initial_value, move |candidate| { if policies.contains(candidate) { Ok(()) } else { @@ -245,12 +284,13 @@ impl TryFrom for ConfigRequirements { field_name: "approval_policy", candidate: format!("{candidate:?}"), allowed: format!("{policies:?}"), - requirement_source: requirement_source.clone(), + requirement_source: requirement_source_for_error.clone(), }) } - })? + })?; + ConstrainedWithSource::new(constrained, Some(requirement_source)) } - None => Constrained::allow_any_from_default(), + None => ConstrainedWithSource::new(Constrained::allow_any_from_default(), None), }; // TODO(gt): `ConfigRequirementsToml` should let the author specify the @@ -261,7 +301,7 @@ impl TryFrom for ConfigRequirements { // additional parameters. Ultimately, we should expand the config // format to allow specifying those parameters. let default_sandbox_policy = SandboxPolicy::ReadOnly; - let sandbox_policy: Constrained = match allowed_sandbox_modes { + let sandbox_policy = match allowed_sandbox_modes { Some(Sourced { value: modes, source: requirement_source, @@ -275,7 +315,8 @@ impl TryFrom for ConfigRequirements { }); }; - Constrained::new(default_sandbox_policy, move |candidate| { + let requirement_source_for_error = requirement_source.clone(); + let constrained = Constrained::new(default_sandbox_policy, move |candidate| { let mode = match candidate { SandboxPolicy::ReadOnly => SandboxModeRequirement::ReadOnly, SandboxPolicy::WorkspaceWrite { .. } => { @@ -293,12 +334,15 @@ impl TryFrom for ConfigRequirements { field_name: "sandbox_mode", candidate: format!("{mode:?}"), allowed: format!("{modes:?}"), - requirement_source: requirement_source.clone(), + requirement_source: requirement_source_for_error.clone(), }) } - })? + })?; + ConstrainedWithSource::new(constrained, Some(requirement_source)) + } + None => { + ConstrainedWithSource::new(Constrained::allow_any(default_sandbox_policy), None) } - None => Constrained::allow_any(default_sandbox_policy), }; let exec_policy = match rules { Some(Sourced { value, source }) => { @@ -313,13 +357,14 @@ impl TryFrom for ConfigRequirements { None => None, }; - let enforce_residency: Constrained> = match enforce_residency { + let enforce_residency = match enforce_residency { Some(Sourced { value: residency, source: requirement_source, }) => { let required = Some(residency); - Constrained::new(required, move |candidate| { + let requirement_source_for_error = requirement_source.clone(); + let constrained = Constrained::new(required, move |candidate| { if candidate == &required { Ok(()) } else { @@ -327,12 +372,13 @@ impl TryFrom for ConfigRequirements { field_name: "enforce_residency", candidate: format!("{candidate:?}"), allowed: format!("{required:?}"), - requirement_source: requirement_source.clone(), + requirement_source: requirement_source_for_error.clone(), }) } - })? + })?; + ConstrainedWithSource::new(constrained, Some(requirement_source)) } - None => Constrained::allow_any(None), + None => ConstrainedWithSource::new(Constrained::allow_any(None), None), }; Ok(ConfigRequirements { approval_policy, @@ -563,6 +609,34 @@ mod tests { Ok(()) } + #[test] + fn constrained_fields_store_requirement_source() -> Result<()> { + let source: ConfigRequirementsToml = from_str( + r#" + allowed_approval_policies = ["on-request"] + allowed_sandbox_modes = ["read-only"] + enforce_residency = "us" + "#, + )?; + + let source_location = RequirementSource::CloudRequirements; + let mut target = ConfigRequirementsWithSources::default(); + target.merge_unset_fields(source_location.clone(), source); + let requirements = ConfigRequirements::try_from(target)?; + + assert_eq!( + requirements.approval_policy.source, + Some(source_location.clone()) + ); + assert_eq!( + requirements.sandbox_policy.source, + Some(source_location.clone()) + ); + assert_eq!(requirements.enforce_residency.source, Some(source_location)); + + Ok(()) + } + #[test] fn deserialize_allowed_approval_policies() -> Result<()> { let toml_str = r#" diff --git a/codex-rs/core/src/config_loader/mod.rs b/codex-rs/core/src/config_loader/mod.rs index d8813e23d7..0ae54111a4 100644 --- a/codex-rs/core/src/config_loader/mod.rs +++ b/codex-rs/core/src/config_loader/mod.rs @@ -34,6 +34,7 @@ use toml::Value as TomlValue; pub use cloud_requirements::CloudRequirementsLoader; pub use config_requirements::ConfigRequirements; pub use config_requirements::ConfigRequirementsToml; +pub use config_requirements::ConstrainedWithSource; pub use config_requirements::McpServerIdentity; pub use config_requirements::McpServerRequirement; pub use config_requirements::RequirementSource; @@ -71,8 +72,8 @@ const DEFAULT_PROJECT_ROOT_MARKERS: &[&str] = &[".git"]; /// configuration layers in the following order, but a constraint defined in an /// earlier layer cannot be overridden by a later layer: /// -/// - admin: managed preferences (*) /// - cloud: managed cloud requirements +/// - admin: managed preferences (*) /// - system `/etc/codex/requirements.toml` /// /// For backwards compatibility, we also load from @@ -106,6 +107,11 @@ pub async fn load_config_layers_state( ) -> io::Result { let mut config_requirements_toml = ConfigRequirementsWithSources::default(); + if let Some(requirements) = cloud_requirements.get().await { + config_requirements_toml + .merge_unset_fields(RequirementSource::CloudRequirements, requirements); + } + #[cfg(target_os = "macos")] macos::load_managed_admin_requirements_toml( &mut config_requirements_toml, @@ -115,11 +121,6 @@ pub async fn load_config_layers_state( ) .await?; - if let Some(requirements) = cloud_requirements.get().await { - config_requirements_toml - .merge_unset_fields(RequirementSource::CloudRequirements, requirements); - } - // Honor /etc/codex/requirements.toml. if cfg!(unix) { load_requirements_toml( @@ -143,7 +144,15 @@ pub async fn load_config_layers_state( let cli_overrides_layer = if cli_overrides.is_empty() { None } else { - Some(overrides::build_cli_overrides_layer(cli_overrides)) + let cli_overrides_layer = overrides::build_cli_overrides_layer(cli_overrides); + let base_dir = cwd + .as_ref() + .map(AbsolutePathBuf::as_path) + .unwrap_or(codex_home); + Some(resolve_relative_paths_in_config_toml( + cli_overrides_layer, + base_dir, + )?) }; // Include an entry for the "system" config folder, loading its config.toml, diff --git a/codex-rs/core/src/config_loader/tests.rs b/codex-rs/core/src/config_loader/tests.rs index 4ea0bd6c43..7375e9e2a7 100644 --- a/codex-rs/core/src/config_loader/tests.rs +++ b/codex-rs/core/src/config_loader/tests.rs @@ -56,6 +56,30 @@ async fn make_config_for_test( .await } +#[tokio::test] +async fn cli_overrides_resolve_relative_paths_against_cwd() -> std::io::Result<()> { + let codex_home = tempdir().expect("tempdir"); + let cwd_dir = tempdir().expect("tempdir"); + let cwd_path = cwd_dir.path().to_path_buf(); + + let config = ConfigBuilder::default() + .codex_home(codex_home.path().to_path_buf()) + .cli_overrides(vec![( + "log_dir".to_string(), + TomlValue::String("run-logs".to_string()), + )]) + .harness_overrides(ConfigOverrides { + cwd: Some(cwd_path.clone()), + ..Default::default() + }) + .build() + .await?; + + let expected = AbsolutePathBuf::resolve_path_against_base("run-logs", cwd_path)?; + assert_eq!(config.log_dir, expected.to_path_buf()); + Ok(()) +} + #[tokio::test] async fn returns_config_error_for_invalid_user_config_toml() { let tmp = tempdir().expect("tempdir"); @@ -487,6 +511,59 @@ enforce_residency = "us" Ok(()) } +#[cfg(target_os = "macos")] +#[tokio::test] +async fn cloud_requirements_take_precedence_over_mdm_requirements() -> anyhow::Result<()> { + use base64::Engine; + + let tmp = tempdir()?; + let state = load_config_layers_state( + tmp.path(), + Some(AbsolutePathBuf::try_from(tmp.path())?), + &[] as &[(String, TomlValue)], + LoaderOverrides { + macos_managed_config_requirements_base64: Some( + base64::prelude::BASE64_STANDARD.encode( + r#" +allowed_approval_policies = ["on-request"] +"# + .as_bytes(), + ), + ), + ..LoaderOverrides::default() + }, + CloudRequirementsLoader::new(async { + Some(ConfigRequirementsToml { + allowed_approval_policies: Some(vec![AskForApproval::Never]), + allowed_sandbox_modes: None, + mcp_servers: None, + rules: None, + enforce_residency: None, + }) + }), + ) + .await?; + + assert_eq!( + state.requirements().approval_policy.value(), + AskForApproval::Never + ); + assert_eq!( + state + .requirements() + .approval_policy + .can_set(&AskForApproval::OnRequest), + Err(ConstraintError::InvalidValue { + field_name: "approval_policy", + candidate: "OnRequest".into(), + allowed: "[Never]".into(), + requirement_source: RequirementSource::CloudRequirements, + }) + ); + + Ok(()) +} + #[tokio::test(flavor = "current_thread")] async fn cloud_requirements_are_not_overwritten_by_system_requirements() -> anyhow::Result<()> { let tmp = tempdir()?; diff --git a/codex-rs/core/src/connectors.rs b/codex-rs/core/src/connectors.rs index edf3c63aa5..afe03fbbe7 100644 --- a/codex-rs/core/src/connectors.rs +++ b/codex-rs/core/src/connectors.rs @@ -43,6 +43,7 @@ pub async fn list_accessible_connectors_from_mcp_tools( sandbox_policy: SandboxPolicy::ReadOnly, codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(), sandbox_cwd: env::current_dir().unwrap_or_else(|_| PathBuf::from("/")), + use_linux_sandbox_bwrap: config.features.enabled(Feature::UseLinuxSandboxBwrap), }; mcp_connection_manager diff --git a/codex-rs/core/src/context_manager/history.rs b/codex-rs/core/src/context_manager/history.rs index a29f7df7e0..a67ea90d3d 100644 --- a/codex-rs/core/src/context_manager/history.rs +++ b/codex-rs/core/src/context_manager/history.rs @@ -9,7 +9,9 @@ use crate::truncate::approx_tokens_from_byte_count; use crate::truncate::truncate_function_output_items_with_policy; use crate::truncate::truncate_text; use crate::user_shell_command::is_user_shell_command_text; +use codex_protocol::models::BaseInstructions; use codex_protocol::models::ContentItem; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::FunctionCallOutputContentItem; use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::ResponseItem; @@ -85,12 +87,20 @@ impl ContextManager { // Estimate token usage using byte-based heuristics from the truncation helpers. // This is a coarse lower bound, not a tokenizer-accurate count. pub(crate) fn estimate_token_count(&self, turn_context: &TurnContext) -> Option { - let model_info = turn_context.client.get_model_info(); - let personality = turn_context - .personality - .or(turn_context.client.config().personality); - let base_instructions = model_info.get_model_instructions(personality); - let base_tokens = i64::try_from(approx_token_count(&base_instructions)).unwrap_or(i64::MAX); + let model_info = &turn_context.model_info; + let personality = turn_context.personality.or(turn_context.config.personality); + let base_instructions = BaseInstructions { + text: model_info.get_model_instructions(personality), + }; + self.estimate_token_count_with_base_instructions(&base_instructions) + } + + pub(crate) fn estimate_token_count_with_base_instructions( + &self, + base_instructions: &BaseInstructions, + ) -> Option { + let base_tokens = + i64::try_from(approx_token_count(&base_instructions.text)).unwrap_or(i64::MAX); let items_tokens = self.items.iter().fold(0i64, |acc, item| { acc.saturating_add(estimate_item_token_count(item)) @@ -136,7 +146,7 @@ impl ContextManager { match &mut self.items[index] { ResponseItem::FunctionCallOutput { output, .. } => { - let Some(content_items) = output.content_items.as_mut() else { + let Some(content_items) = output.content_items_mut() else { return false; }; let mut replaced = false; @@ -270,19 +280,23 @@ impl ContextManager { let policy_with_serialization_budget = policy * 1.2; match item { ResponseItem::FunctionCallOutput { call_id, output } => { - let truncated = - truncate_text(output.content.as_str(), policy_with_serialization_budget); - let truncated_items = output.content_items.as_ref().map(|items| { - truncate_function_output_items_with_policy( - items, - policy_with_serialization_budget, - ) - }); + let body = match &output.body { + FunctionCallOutputBody::Text(content) => FunctionCallOutputBody::Text( + truncate_text(content, policy_with_serialization_budget), + ), + FunctionCallOutputBody::ContentItems(items) => { + FunctionCallOutputBody::ContentItems( + truncate_function_output_items_with_policy( + items, + policy_with_serialization_budget, + ), + ) + } + }; ResponseItem::FunctionCallOutput { call_id: call_id.clone(), output: FunctionCallOutputPayload { - content: truncated, - content_items: truncated_items, + body, success: output.success, }, } diff --git a/codex-rs/core/src/context_manager/history_tests.rs b/codex-rs/core/src/context_manager/history_tests.rs index a6eba62f1a..87defcd1d6 100644 --- a/codex-rs/core/src/context_manager/history_tests.rs +++ b/codex-rs/core/src/context_manager/history_tests.rs @@ -2,7 +2,9 @@ use super::*; use crate::truncate; use crate::truncate::TruncationPolicy; use codex_git::GhostCommit; +use codex_protocol::models::BaseInstructions; use codex_protocol::models::ContentItem; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::FunctionCallOutputContentItem; use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::LocalShellAction; @@ -63,10 +65,7 @@ fn user_input_text_msg(text: &str) -> ResponseItem { fn function_call_output(call_id: &str, content: &str) -> ResponseItem { ResponseItem::FunctionCallOutput { call_id: call_id.to_string(), - output: FunctionCallOutputPayload { - content: content.to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text(content.to_string()), } } @@ -105,6 +104,10 @@ fn truncate_exec_output(content: &str) -> String { truncate::truncate_text(content, TruncationPolicy::Tokens(EXEC_FORMAT_MAX_TOKENS)) } +fn approx_token_count_for_text(text: &str) -> i64 { + i64::try_from(text.len().saturating_add(3) / 4).unwrap_or(i64::MAX) +} + #[test] fn filters_non_api_messages() { let mut h = ContextManager::default(); @@ -252,6 +255,28 @@ fn get_history_for_prompt_drops_ghost_commits() { assert_eq!(filtered, vec![]); } +#[test] +fn estimate_token_count_with_base_instructions_uses_provided_text() { + let history = create_history_with_items(vec![assistant_msg("hello from history")]); + let short_base = BaseInstructions { + text: "short".to_string(), + }; + let long_base = BaseInstructions { + text: "x".repeat(1_000), + }; + + let short_estimate = history + .estimate_token_count_with_base_instructions(&short_base) + .expect("token estimate"); + let long_estimate = history + .estimate_token_count_with_base_instructions(&long_base) + .expect("token estimate"); + + let expected_delta = approx_token_count_for_text(&long_base.text) + - approx_token_count_for_text(&short_base.text); + assert_eq!(long_estimate - short_estimate, expected_delta); +} + #[test] fn remove_first_item_removes_matching_output_for_function_call() { let items = vec![ @@ -263,10 +288,7 @@ fn remove_first_item_removes_matching_output_for_function_call() { }, ResponseItem::FunctionCallOutput { call_id: "call-1".to_string(), - output: FunctionCallOutputPayload { - content: "ok".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("ok".to_string()), }, ]; let mut h = create_history_with_items(items); @@ -279,10 +301,7 @@ fn remove_first_item_removes_matching_call_for_output() { let items = vec![ ResponseItem::FunctionCallOutput { call_id: "call-2".to_string(), - output: FunctionCallOutputPayload { - content: "ok".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("ok".to_string()), }, ResponseItem::FunctionCall { id: None, @@ -308,10 +327,7 @@ fn remove_last_item_removes_matching_call_for_output() { }, ResponseItem::FunctionCallOutput { call_id: "call-delete-last".to_string(), - output: FunctionCallOutputPayload { - content: "ok".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("ok".to_string()), }, ]; let mut h = create_history_with_items(items); @@ -327,10 +343,11 @@ fn replace_last_turn_images_replaces_tool_output_images() { ResponseItem::FunctionCallOutput { call_id: "call-1".to_string(), output: FunctionCallOutputPayload { - content: "ok".to_string(), - content_items: Some(vec![FunctionCallOutputContentItem::InputImage { - image_url: "data:image/png;base64,AAA".to_string(), - }]), + body: FunctionCallOutputBody::ContentItems(vec![ + FunctionCallOutputContentItem::InputImage { + image_url: "data:image/png;base64,AAA".to_string(), + }, + ]), success: Some(true), }, }, @@ -346,10 +363,11 @@ fn replace_last_turn_images_replaces_tool_output_images() { ResponseItem::FunctionCallOutput { call_id: "call-1".to_string(), output: FunctionCallOutputPayload { - content: "ok".to_string(), - content_items: Some(vec![FunctionCallOutputContentItem::InputText { - text: "Invalid image".to_string(), - }]), + body: FunctionCallOutputBody::ContentItems(vec![ + FunctionCallOutputContentItem::InputText { + text: "Invalid image".to_string(), + }, + ]), success: Some(true), }, }, @@ -391,10 +409,7 @@ fn remove_first_item_handles_local_shell_pair() { }, ResponseItem::FunctionCallOutput { call_id: "call-3".to_string(), - output: FunctionCallOutputPayload { - content: "ok".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("ok".to_string()), }, ]; let mut h = create_history_with_items(items); @@ -560,10 +575,7 @@ fn normalization_retains_local_shell_outputs() { }, ResponseItem::FunctionCallOutput { call_id: "shell-1".to_string(), - output: FunctionCallOutputPayload { - content: "Total output lines: 1\n\nok".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("Total output lines: 1\n\nok".to_string()), }, ]; @@ -583,9 +595,8 @@ fn record_items_truncates_function_call_output_content() { let item = ResponseItem::FunctionCallOutput { call_id: "call-100".to_string(), output: FunctionCallOutputPayload { - content: long_output.clone(), + body: FunctionCallOutputBody::Text(long_output.clone()), success: Some(true), - ..Default::default() }, }; @@ -594,16 +605,15 @@ fn record_items_truncates_function_call_output_content() { assert_eq!(history.items.len(), 1); match &history.items[0] { ResponseItem::FunctionCallOutput { output, .. } => { - assert_ne!(output.content, long_output); + let content = output.text_content().unwrap_or_default(); + assert_ne!(content, long_output); assert!( - output.content.contains("tokens truncated"), - "expected token-based truncation marker, got {}", - output.content + content.contains("tokens truncated"), + "expected token-based truncation marker, got {content}" ); assert!( - output.content.contains("tokens truncated"), - "expected truncation marker, got {}", - output.content + content.contains("tokens truncated"), + "expected truncation marker, got {content}" ); } other => panic!("unexpected history item: {other:?}"), @@ -648,9 +658,8 @@ fn record_items_respects_custom_token_limit() { let item = ResponseItem::FunctionCallOutput { call_id: "call-custom-limit".to_string(), output: FunctionCallOutputPayload { - content: long_output, + body: FunctionCallOutputBody::Text(long_output), success: Some(true), - ..Default::default() }, }; @@ -660,7 +669,11 @@ fn record_items_respects_custom_token_limit() { ResponseItem::FunctionCallOutput { output, .. } => output, other => panic!("unexpected history item: {other:?}"), }; - assert!(stored.content.contains("tokens truncated")); + assert!( + stored + .text_content() + .is_some_and(|content| content.contains("tokens truncated")) + ); } fn assert_truncated_message_matches(message: &str, line: &str, expected_removed: usize) { @@ -782,10 +795,7 @@ fn normalize_adds_missing_output_for_function_call() { }, ResponseItem::FunctionCallOutput { call_id: "call-x".to_string(), - output: FunctionCallOutputPayload { - content: "aborted".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("aborted".to_string()), }, ] ); @@ -859,10 +869,7 @@ fn normalize_adds_missing_output_for_local_shell_call_with_id() { }, ResponseItem::FunctionCallOutput { call_id: "shell-1".to_string(), - output: FunctionCallOutputPayload { - content: "aborted".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("aborted".to_string()), }, ] ); @@ -873,10 +880,7 @@ fn normalize_adds_missing_output_for_local_shell_call_with_id() { fn normalize_removes_orphan_function_call_output() { let items = vec![ResponseItem::FunctionCallOutput { call_id: "orphan-1".to_string(), - output: FunctionCallOutputPayload { - content: "ok".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("ok".to_string()), }]; let mut h = create_history_with_items(items); @@ -913,10 +917,7 @@ fn normalize_mixed_inserts_and_removals() { // Orphan output that should be removed ResponseItem::FunctionCallOutput { call_id: "c2".to_string(), - output: FunctionCallOutputPayload { - content: "ok".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("ok".to_string()), }, // Will get an inserted custom tool output ResponseItem::CustomToolCall { @@ -955,10 +956,7 @@ fn normalize_mixed_inserts_and_removals() { }, ResponseItem::FunctionCallOutput { call_id: "c1".to_string(), - output: FunctionCallOutputPayload { - content: "aborted".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("aborted".to_string()), }, ResponseItem::CustomToolCall { id: None, @@ -985,10 +983,7 @@ fn normalize_mixed_inserts_and_removals() { }, ResponseItem::FunctionCallOutput { call_id: "s1".to_string(), - output: FunctionCallOutputPayload { - content: "aborted".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("aborted".to_string()), }, ] ); @@ -1015,10 +1010,7 @@ fn normalize_adds_missing_output_for_function_call_inserts_output() { }, ResponseItem::FunctionCallOutput { call_id: "call-x".to_string(), - output: FunctionCallOutputPayload { - content: "aborted".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("aborted".to_string()), }, ] ); @@ -1065,10 +1057,7 @@ fn normalize_adds_missing_output_for_local_shell_call_with_id_panics_in_debug() fn normalize_removes_orphan_function_call_output_panics_in_debug() { let items = vec![ResponseItem::FunctionCallOutput { call_id: "orphan-1".to_string(), - output: FunctionCallOutputPayload { - content: "ok".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("ok".to_string()), }]; let mut h = create_history_with_items(items); h.normalize_history(); @@ -1099,10 +1088,7 @@ fn normalize_mixed_inserts_and_removals_panics_in_debug() { }, ResponseItem::FunctionCallOutput { call_id: "c2".to_string(), - output: FunctionCallOutputPayload { - content: "ok".to_string(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("ok".to_string()), }, ResponseItem::CustomToolCall { id: None, diff --git a/codex-rs/core/src/context_manager/normalize.rs b/codex-rs/core/src/context_manager/normalize.rs index 85e25e32aa..37e177900f 100644 --- a/codex-rs/core/src/context_manager/normalize.rs +++ b/codex-rs/core/src/context_manager/normalize.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::ResponseItem; @@ -29,7 +30,7 @@ pub(crate) fn ensure_call_outputs_present(items: &mut Vec) { ResponseItem::FunctionCallOutput { call_id: call_id.clone(), output: FunctionCallOutputPayload { - content: "aborted".to_string(), + body: FunctionCallOutputBody::Text("aborted".to_string()), ..Default::default() }, }, @@ -76,7 +77,7 @@ pub(crate) fn ensure_call_outputs_present(items: &mut Vec) { ResponseItem::FunctionCallOutput { call_id: call_id.clone(), output: FunctionCallOutputPayload { - content: "aborted".to_string(), + body: FunctionCallOutputBody::Text("aborted".to_string()), ..Default::default() }, }, diff --git a/codex-rs/core/src/exec.rs b/codex-rs/core/src/exec.rs index e0d65c8305..d4d508ced6 100644 --- a/codex-rs/core/src/exec.rs +++ b/codex-rs/core/src/exec.rs @@ -128,6 +128,17 @@ pub enum SandboxType { WindowsRestrictedToken, } +impl SandboxType { + pub(crate) fn as_metric_tag(self) -> &'static str { + match self { + SandboxType::None => "none", + SandboxType::MacosSeatbelt => "seatbelt", + SandboxType::LinuxSeccomp => "seccomp", + SandboxType::WindowsRestrictedToken => "windows_sandbox", + } + } +} + #[derive(Clone)] pub struct StdoutStream { pub sub_id: String, @@ -140,6 +151,7 @@ pub async fn process_exec_tool_call( sandbox_policy: &SandboxPolicy, sandbox_cwd: &Path, codex_linux_sandbox_exe: &Option, + use_linux_sandbox_bwrap: bool, stdout_stream: Option, ) -> Result { let windows_sandbox_level = params.windows_sandbox_level; @@ -184,14 +196,15 @@ pub async fn process_exec_tool_call( let manager = SandboxManager::new(); let exec_env = manager - .transform( + .transform(crate::sandboxing::SandboxTransformRequest { spec, - sandbox_policy, - sandbox_type, - sandbox_cwd, - codex_linux_sandbox_exe.as_ref(), + policy: sandbox_policy, + sandbox: sandbox_type, + sandbox_policy_cwd: sandbox_cwd, + codex_linux_sandbox_exe: codex_linux_sandbox_exe.as_ref(), + use_linux_sandbox_bwrap, windows_sandbox_level, - ) + }) .map_err(CodexErr::from)?; // Route through the sandboxing module for a single, unified execution path. @@ -1108,6 +1121,7 @@ mod tests { &SandboxPolicy::DangerFullAccess, cwd.as_path(), &None, + false, None, ) .await; diff --git a/codex-rs/core/src/features.rs b/codex-rs/core/src/features.rs index 1eb2a2dea5..ee091979eb 100644 --- a/codex-rs/core/src/features.rs +++ b/codex-rs/core/src/features.rs @@ -89,6 +89,8 @@ pub enum Feature { WebSearchCached, /// Gate the execpolicy enforcement for shell/unified exec. ExecPolicy, + /// Use the bubblewrap-based Linux sandbox pipeline. + UseLinuxSandboxBwrap, /// Allow the model to request approval and propose exec rules. RequestRule, /// Enable Windows sandbox (restricted token) on Windows. @@ -105,6 +107,8 @@ pub enum Feature { RuntimeMetrics, /// Persist rollout metadata to a local SQLite database. Sqlite, + /// Enable the get_memory tool backed by SQLite thread memories. + MemoryTool, /// Append additional AGENTS.md guidance to user instructions. ChildAgentsMd, /// Enforce UTF8 output in Powershell. @@ -406,6 +410,12 @@ pub const FEATURES: &[FeatureSpec] = &[ stage: Stage::Stable, default_enabled: true, }, + FeatureSpec { + id: Feature::UnifiedExec, + key: "unified_exec", + stage: Stage::Stable, + default_enabled: !cfg!(windows), + }, FeatureSpec { id: Feature::WebSearchRequest, key: "web_search_request", @@ -419,16 +429,6 @@ pub const FEATURES: &[FeatureSpec] = &[ default_enabled: false, }, // Experimental program. Rendered in the `/experimental` menu for users. - FeatureSpec { - id: Feature::UnifiedExec, - key: "unified_exec", - stage: Stage::Experimental { - name: "Background terminal", - menu_description: "Run long-running terminal commands in the background.", - announcement: "NEW! Try Background terminals for long-running commands. Enable in /experimental!", - }, - default_enabled: false, - }, FeatureSpec { id: Feature::ShellSnapshot, key: "shell_snapshot", @@ -451,6 +451,12 @@ pub const FEATURES: &[FeatureSpec] = &[ stage: Stage::UnderDevelopment, default_enabled: false, }, + FeatureSpec { + id: Feature::MemoryTool, + key: "memory_tool", + stage: Stage::UnderDevelopment, + default_enabled: false, + }, FeatureSpec { id: Feature::ChildAgentsMd, key: "child_agents_md", @@ -469,6 +475,12 @@ pub const FEATURES: &[FeatureSpec] = &[ stage: Stage::UnderDevelopment, default_enabled: true, }, + FeatureSpec { + id: Feature::UseLinuxSandboxBwrap, + key: "use_linux_sandbox_bwrap", + stage: Stage::UnderDevelopment, + default_enabled: false, + }, FeatureSpec { id: Feature::RequestRule, key: "request_rule", @@ -552,12 +564,8 @@ pub const FEATURES: &[FeatureSpec] = &[ FeatureSpec { id: Feature::Steer, key: "steer", - stage: Stage::Experimental { - name: "Steer conversation", - menu_description: "Enter submits immediately; Tab queues messages when a task is running.", - announcement: "NEW! Try Steer mode: Enter submits immediately, Tab queues. Enable in /experimental!", - }, - default_enabled: false, + stage: Stage::Stable, + default_enabled: true, }, FeatureSpec { id: Feature::CollaborationModes, diff --git a/codex-rs/core/src/file_watcher.rs b/codex-rs/core/src/file_watcher.rs new file mode 100644 index 0000000000..afda5b6086 --- /dev/null +++ b/codex-rs/core/src/file_watcher.rs @@ -0,0 +1,407 @@ +//! Watches skill roots for changes and broadcasts coarse-grained +//! `FileWatcherEvent`s that higher-level components react to on the next turn. + +use std::collections::HashMap; +use std::collections::HashSet; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; +use std::sync::Mutex; +use std::sync::RwLock; +use std::time::Duration; + +use notify::Event; +use notify::RecommendedWatcher; +use notify::RecursiveMode; +use notify::Watcher; +use tokio::runtime::Handle; +use tokio::sync::broadcast; +use tokio::sync::mpsc; +use tokio::time::Instant; +use tokio::time::sleep_until; +use tracing::warn; + +use crate::config::Config; +use crate::skills::loader::skill_roots_from_layer_stack_with_agents; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum FileWatcherEvent { + SkillsChanged { paths: Vec }, +} + +struct WatchState { + skills_roots: HashSet, +} + +struct FileWatcherInner { + watcher: RecommendedWatcher, + watched_paths: HashMap, +} + +const WATCHER_THROTTLE_INTERVAL: Duration = Duration::from_secs(1); + +/// Coalesces bursts of paths and emits at most once per interval. +struct ThrottledPaths { + pending: HashSet, + next_allowed_at: Instant, +} + +impl ThrottledPaths { + fn new(now: Instant) -> Self { + Self { + pending: HashSet::new(), + next_allowed_at: now, + } + } + + fn add(&mut self, paths: Vec) { + self.pending.extend(paths); + } + + fn next_deadline(&self, now: Instant) -> Option { + (!self.pending.is_empty() && now < self.next_allowed_at).then_some(self.next_allowed_at) + } + + fn take_ready(&mut self, now: Instant) -> Option> { + if self.pending.is_empty() || now < self.next_allowed_at { + return None; + } + Some(self.take_with_next_allowed(now)) + } + + fn take_pending(&mut self, now: Instant) -> Option> { + if self.pending.is_empty() { + return None; + } + Some(self.take_with_next_allowed(now)) + } + + fn take_with_next_allowed(&mut self, now: Instant) -> Vec { + let mut paths: Vec = self.pending.drain().collect(); + paths.sort_unstable_by(|a, b| a.as_os_str().cmp(b.as_os_str())); + self.next_allowed_at = now + WATCHER_THROTTLE_INTERVAL; + paths + } +} + +pub(crate) struct FileWatcher { + inner: Option>, + state: Arc>, + tx: broadcast::Sender, +} + +impl FileWatcher { + pub(crate) fn new(_codex_home: PathBuf) -> notify::Result { + let (raw_tx, raw_rx) = mpsc::unbounded_channel(); + let raw_tx_clone = raw_tx; + let watcher = notify::recommended_watcher(move |res| { + let _ = raw_tx_clone.send(res); + })?; + let inner = FileWatcherInner { + watcher, + watched_paths: HashMap::new(), + }; + let (tx, _) = broadcast::channel(128); + let state = Arc::new(RwLock::new(WatchState { + skills_roots: HashSet::new(), + })); + let file_watcher = Self { + inner: Some(Mutex::new(inner)), + state: Arc::clone(&state), + tx: tx.clone(), + }; + file_watcher.spawn_event_loop(raw_rx, state, tx); + Ok(file_watcher) + } + + pub(crate) fn noop() -> Self { + let (tx, _) = broadcast::channel(1); + Self { + inner: None, + state: Arc::new(RwLock::new(WatchState { + skills_roots: HashSet::new(), + })), + tx, + } + } + + pub(crate) fn subscribe(&self) -> broadcast::Receiver { + self.tx.subscribe() + } + + pub(crate) fn register_config(&self, config: &Config) { + let roots = + skill_roots_from_layer_stack_with_agents(&config.config_layer_stack, &config.cwd); + for root in roots { + self.register_skills_root(root.path); + } + } + + // Bridge `notify`'s callback-based events into the Tokio runtime and + // broadcast coarse-grained change signals to subscribers. + fn spawn_event_loop( + &self, + mut raw_rx: mpsc::UnboundedReceiver>, + state: Arc>, + tx: broadcast::Sender, + ) { + if let Ok(handle) = Handle::try_current() { + handle.spawn(async move { + let now = Instant::now(); + let mut skills = ThrottledPaths::new(now); + + loop { + let now = Instant::now(); + let next_deadline = skills.next_deadline(now); + let timer_deadline = next_deadline + .unwrap_or_else(|| now + Duration::from_secs(60 * 60 * 24 * 365)); + let timer = sleep_until(timer_deadline); + tokio::pin!(timer); + + tokio::select! { + res = raw_rx.recv() => { + match res { + Some(Ok(event)) => { + let skills_paths = classify_event(&event, &state); + let now = Instant::now(); + skills.add(skills_paths); + + if let Some(paths) = skills.take_ready(now) { + let _ = tx.send(FileWatcherEvent::SkillsChanged { paths }); + } + } + Some(Err(err)) => { + warn!("file watcher error: {err}"); + } + None => { + // Flush any pending changes before shutdown so subscribers + // see the latest state. + let now = Instant::now(); + if let Some(paths) = skills.take_pending(now) { + let _ = tx.send(FileWatcherEvent::SkillsChanged { paths }); + } + break; + } + } + } + _ = &mut timer => { + let now = Instant::now(); + if let Some(paths) = skills.take_ready(now) { + let _ = tx.send(FileWatcherEvent::SkillsChanged { paths }); + } + } + } + } + }); + } else { + warn!("file watcher loop skipped: no Tokio runtime available"); + } + } + + fn register_skills_root(&self, root: PathBuf) { + { + let mut state = match self.state.write() { + Ok(state) => state, + Err(err) => err.into_inner(), + }; + state.skills_roots.insert(root.clone()); + } + self.watch_path(root, RecursiveMode::Recursive); + } + + fn watch_path(&self, path: PathBuf, mode: RecursiveMode) { + let Some(inner) = &self.inner else { + return; + }; + if !path.exists() { + return; + } + let watch_path = path; + let mut guard = match inner.lock() { + Ok(guard) => guard, + Err(err) => err.into_inner(), + }; + if let Some(existing) = guard.watched_paths.get(&watch_path) { + if *existing == RecursiveMode::Recursive || *existing == mode { + return; + } + if let Err(err) = guard.watcher.unwatch(&watch_path) { + warn!("failed to unwatch {}: {err}", watch_path.display()); + } + } + if let Err(err) = guard.watcher.watch(&watch_path, mode) { + warn!("failed to watch {}: {err}", watch_path.display()); + return; + } + guard.watched_paths.insert(watch_path, mode); + } +} + +fn classify_event(event: &Event, state: &RwLock) -> Vec { + let mut skills_paths = Vec::new(); + let skills_roots = match state.read() { + Ok(state) => state.skills_roots.clone(), + Err(err) => { + let state = err.into_inner(); + state.skills_roots.clone() + } + }; + + for path in &event.paths { + if is_skills_path(path, &skills_roots) { + skills_paths.push(path.clone()); + } + } + + skills_paths +} + +fn is_skills_path(path: &Path, roots: &HashSet) -> bool { + roots.iter().any(|root| path.starts_with(root)) +} + +#[cfg(test)] +mod tests { + use super::*; + use notify::EventKind; + use pretty_assertions::assert_eq; + use tokio::time::timeout; + + fn path(name: &str) -> PathBuf { + PathBuf::from(name) + } + + fn notify_event(paths: Vec) -> Event { + let mut event = Event::new(EventKind::Any); + for path in paths { + event = event.add_path(path); + } + event + } + + #[test] + fn throttles_and_coalesces_within_interval() { + let start = Instant::now(); + let mut throttled = ThrottledPaths::new(start); + + throttled.add(vec![path("a")]); + let first = throttled.take_ready(start).expect("first emit"); + assert_eq!(first, vec![path("a")]); + + throttled.add(vec![path("b"), path("c")]); + assert_eq!(throttled.take_ready(start), None); + + let second = throttled + .take_ready(start + WATCHER_THROTTLE_INTERVAL) + .expect("coalesced emit"); + assert_eq!(second, vec![path("b"), path("c")]); + } + + #[test] + fn flushes_pending_on_shutdown() { + let start = Instant::now(); + let mut throttled = ThrottledPaths::new(start); + + throttled.add(vec![path("a")]); + let _ = throttled.take_ready(start).expect("first emit"); + + throttled.add(vec![path("b")]); + assert_eq!(throttled.take_ready(start), None); + + let flushed = throttled + .take_pending(start) + .expect("shutdown flush emits pending paths"); + assert_eq!(flushed, vec![path("b")]); + } + + #[test] + fn classify_event_filters_to_skills_roots() { + let root = path("/tmp/skills"); + let state = RwLock::new(WatchState { + skills_roots: HashSet::from([root.clone()]), + }); + let event = notify_event(vec![ + root.join("demo/SKILL.md"), + path("/tmp/other/not-a-skill.txt"), + ]); + + let classified = classify_event(&event, &state); + assert_eq!(classified, vec![root.join("demo/SKILL.md")]); + } + + #[test] + fn classify_event_supports_multiple_roots_without_prefix_false_positives() { + let root_a = path("/tmp/skills"); + let root_b = path("/tmp/workspace/.codex/skills"); + let state = RwLock::new(WatchState { + skills_roots: HashSet::from([root_a.clone(), root_b.clone()]), + }); + let event = notify_event(vec![ + root_a.join("alpha/SKILL.md"), + path("/tmp/skills-extra/not-under-skills.txt"), + root_b.join("beta/SKILL.md"), + ]); + + let classified = classify_event(&event, &state); + assert_eq!( + classified, + vec![root_a.join("alpha/SKILL.md"), root_b.join("beta/SKILL.md")] + ); + } + + #[test] + fn register_skills_root_dedupes_state_entries() { + let watcher = FileWatcher::noop(); + let root = path("/tmp/skills"); + watcher.register_skills_root(root.clone()); + watcher.register_skills_root(root); + watcher.register_skills_root(path("/tmp/other-skills")); + + let state = watcher.state.read().expect("state lock"); + assert_eq!(state.skills_roots.len(), 2); + } + + #[tokio::test] + async fn spawn_event_loop_flushes_pending_changes_on_shutdown() { + let watcher = FileWatcher::noop(); + let root = path("/tmp/skills"); + { + let mut state = watcher.state.write().expect("state lock"); + state.skills_roots.insert(root.clone()); + } + + let (raw_tx, raw_rx) = mpsc::unbounded_channel(); + let (tx, mut rx) = broadcast::channel(8); + watcher.spawn_event_loop(raw_rx, Arc::clone(&watcher.state), tx); + + raw_tx + .send(Ok(notify_event(vec![root.join("a/SKILL.md")]))) + .expect("send first event"); + let first = timeout(Duration::from_secs(2), rx.recv()) + .await + .expect("first watcher event") + .expect("broadcast recv first"); + assert_eq!( + first, + FileWatcherEvent::SkillsChanged { + paths: vec![root.join("a/SKILL.md")] + } + ); + + raw_tx + .send(Ok(notify_event(vec![root.join("b/SKILL.md")]))) + .expect("send second event"); + drop(raw_tx); + + let second = timeout(Duration::from_secs(2), rx.recv()) + .await + .expect("second watcher event") + .expect("broadcast recv second"); + assert_eq!( + second, + FileWatcherEvent::SkillsChanged { + paths: vec![root.join("b/SKILL.md")] + } + ); + } +} diff --git a/codex-rs/core/src/hooks/mod.rs b/codex-rs/core/src/hooks/mod.rs new file mode 100644 index 0000000000..2c0612825d --- /dev/null +++ b/codex-rs/core/src/hooks/mod.rs @@ -0,0 +1,8 @@ +mod registry; +mod types; +mod user_notification; + +pub(crate) use registry::Hooks; +pub(crate) use types::HookEvent; +pub(crate) use types::HookEventAfterAgent; +pub(crate) use types::HookPayload; diff --git a/codex-rs/core/src/hooks/registry.rs b/codex-rs/core/src/hooks/registry.rs new file mode 100644 index 0000000000..6bccee85c6 --- /dev/null +++ b/codex-rs/core/src/hooks/registry.rs @@ -0,0 +1,315 @@ +use tokio::process::Command; + +use super::types::Hook; +use super::types::HookEvent; +use super::types::HookOutcome; +use super::types::HookPayload; +use super::user_notification::notify_hook; +use crate::config::Config; + +#[derive(Default, Clone)] +pub(crate) struct Hooks { + after_agent: Vec, +} + +fn get_notify_hook(config: &Config) -> Option { + config + .notify + .as_ref() + .filter(|argv| !argv.is_empty() && !argv[0].is_empty()) + .map(|argv| notify_hook(argv.clone())) +} + +// Hooks are arbitrary, user-specified functions that are deterministically +// executed after specific events in the Codex lifecycle. +impl Hooks { + // new creates a new Hooks instance from config. + // For legacy compatibility, if config.notify is set, it will be added to + // the after_agent hooks. + pub(crate) fn new(config: &Config) -> Self { + let after_agent = get_notify_hook(config).into_iter().collect(); + Self { after_agent } + } + + fn hooks_for_event(&self, hook_event: &HookEvent) -> &[Hook] { + match hook_event { + HookEvent::AfterAgent { .. } => &self.after_agent, + } + } + + pub(crate) async fn dispatch(&self, hook_payload: HookPayload) { + // TODO(gt): support interrupting program execution by returning a result here. + for hook in self.hooks_for_event(&hook_payload.hook_event) { + let outcome = hook.execute(&hook_payload).await; + if matches!(outcome, HookOutcome::Stop) { + break; + } + } + } +} + +pub(super) fn command_from_argv(argv: &[String]) -> Option { + let (program, args) = argv.split_first()?; + if program.is_empty() { + return None; + } + let mut command = Command::new(program); + command.args(args); + Some(command) +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::path::PathBuf; + use std::process::Stdio; + use std::sync::Arc; + use std::sync::atomic::AtomicUsize; + use std::sync::atomic::Ordering; + use std::time::Duration; + + use anyhow::Result; + use chrono::TimeZone; + use chrono::Utc; + use codex_protocol::ThreadId; + use pretty_assertions::assert_eq; + use serde_json::to_string; + use tempfile::tempdir; + use tokio::time::timeout; + + use crate::config::test_config; + + use super::super::types::Hook; + use super::super::types::HookEvent; + use super::super::types::HookEventAfterAgent; + use super::super::types::HookOutcome; + use super::super::types::HookPayload; + use super::Hooks; + use super::command_from_argv; + use super::get_notify_hook; + + const CWD: &str = "/tmp"; + const INPUT_MESSAGE: &str = "hello"; + + fn hook_payload(label: &str) -> HookPayload { + HookPayload { + session_id: ThreadId::new(), + cwd: PathBuf::from(CWD), + triggered_at: Utc + .with_ymd_and_hms(2025, 1, 1, 0, 0, 0) + .single() + .expect("valid timestamp"), + hook_event: HookEvent::AfterAgent { + event: HookEventAfterAgent { + thread_id: ThreadId::new(), + turn_id: format!("turn-{label}"), + input_messages: vec![INPUT_MESSAGE.to_string()], + last_assistant_message: Some("hi".to_string()), + }, + }, + } + } + + fn counting_hook(calls: &Arc, outcome: HookOutcome) -> Hook { + let calls = Arc::clone(calls); + Hook { + func: Arc::new(move |_| { + let calls = Arc::clone(&calls); + Box::pin(async move { + calls.fetch_add(1, Ordering::SeqCst); + outcome + }) + }), + } + } + + fn hooks_for_after_agent(hooks: Vec) -> Hooks { + Hooks { after_agent: hooks } + } + + #[test] + fn command_from_argv_returns_none_for_empty_args() { + assert!(command_from_argv(&[]).is_none()); + assert!(command_from_argv(&["".to_string()]).is_none()); + } + + #[tokio::test] + async fn command_from_argv_builds_command() -> Result<()> { + let argv = if cfg!(windows) { + vec![ + "cmd".to_string(), + "/C".to_string(), + "echo hello world".to_string(), + ] + } else { + vec!["echo".to_string(), "hello".to_string(), "world".to_string()] + }; + let mut command = command_from_argv(&argv).ok_or_else(|| anyhow::anyhow!("command"))?; + let output = command.stdout(Stdio::piped()).output().await?; + + let stdout = String::from_utf8_lossy(&output.stdout); + let trimmed = stdout.trim_end_matches(['\r', '\n']); + assert_eq!(trimmed, "hello world"); + Ok(()) + } + + #[test] + fn get_notify_hook_requires_program_name() { + let mut config = test_config(); + + config.notify = Some(vec![]); + assert!(get_notify_hook(&config).is_none()); + + config.notify = Some(vec!["".to_string()]); + assert!(get_notify_hook(&config).is_none()); + + config.notify = Some(vec!["notify-send".to_string()]); + assert!(get_notify_hook(&config).is_some()); + } + + #[tokio::test] + async fn dispatch_executes_hook() { + let calls = Arc::new(AtomicUsize::new(0)); + let hooks = hooks_for_after_agent(vec![counting_hook(&calls, HookOutcome::Continue)]); + + hooks.dispatch(hook_payload("1")).await; + assert_eq!(calls.load(Ordering::SeqCst), 1); + } + + #[tokio::test] + async fn default_hook_is_noop_and_continues() { + let payload = hook_payload("d"); + let outcome = Hook::default().execute(&payload).await; + assert_eq!(outcome, HookOutcome::Continue); + } + + #[tokio::test] + async fn dispatch_executes_multiple_hooks_for_same_event() { + let calls = Arc::new(AtomicUsize::new(0)); + let hooks = hooks_for_after_agent(vec![ + counting_hook(&calls, HookOutcome::Continue), + counting_hook(&calls, HookOutcome::Continue), + ]); + + hooks.dispatch(hook_payload("2")).await; + assert_eq!(calls.load(Ordering::SeqCst), 2); + } + + #[tokio::test] + async fn dispatch_stops_when_hook_returns_stop() { + let calls = Arc::new(AtomicUsize::new(0)); + let hooks = hooks_for_after_agent(vec![ + counting_hook(&calls, HookOutcome::Stop), + counting_hook(&calls, HookOutcome::Continue), + ]); + + hooks.dispatch(hook_payload("3")).await; + assert_eq!(calls.load(Ordering::SeqCst), 1); + } + + #[cfg(not(windows))] + #[tokio::test] + async fn hook_executes_program_with_payload_argument_unix() -> Result<()> { + let temp_dir = tempdir()?; + let payload_path = temp_dir.path().join("payload.json"); + let payload_path_arg = payload_path.to_string_lossy().into_owned(); + let hook = Hook { + func: Arc::new(move |payload: &HookPayload| { + let payload_path_arg = payload_path_arg.clone(); + Box::pin(async move { + let json = to_string(payload).expect("serialize hook payload"); + let mut command = command_from_argv(&[ + "/bin/sh".to_string(), + "-c".to_string(), + "printf '%s' \"$2\" > \"$1\"".to_string(), + "sh".to_string(), + payload_path_arg, + json, + ]) + .expect("build command"); + command.status().await.expect("run hook command"); + HookOutcome::Continue + }) + }), + }; + + let payload = hook_payload("4"); + let expected = to_string(&payload)?; + + let hooks = hooks_for_after_agent(vec![hook]); + hooks.dispatch(payload).await; + + let contents = timeout(Duration::from_secs(2), async { + loop { + if let Ok(contents) = fs::read_to_string(&payload_path) + && !contents.is_empty() + { + return contents; + } + tokio::time::sleep(Duration::from_millis(10)).await; + } + }) + .await?; + + assert_eq!(contents, expected); + Ok(()) + } + + #[cfg(windows)] + #[tokio::test] + async fn hook_executes_program_with_payload_argument_windows() -> Result<()> { + let temp_dir = tempdir()?; + let payload_path = temp_dir.path().join("payload.json"); + let payload_path_arg = payload_path.to_string_lossy().into_owned(); + let script_path = temp_dir.path().join("write_payload.ps1"); + fs::write(&script_path, "[IO.File]::WriteAllText($args[0], $args[1])")?; + let script_path_arg = script_path.to_string_lossy().into_owned(); + let hook = Hook { + func: Arc::new(move |payload: &HookPayload| { + let payload_path_arg = payload_path_arg.clone(); + let script_path_arg = script_path_arg.clone(); + Box::pin(async move { + let json = to_string(payload).expect("serialize hook payload"); + let powershell = crate::powershell::try_find_powershell_executable_blocking() + .map(|path| path.to_string_lossy().into_owned()) + .unwrap_or_else(|| "powershell.exe".to_string()); + let mut command = command_from_argv(&[ + powershell, + "-NoLogo".to_string(), + "-NoProfile".to_string(), + "-ExecutionPolicy".to_string(), + "Bypass".to_string(), + "-File".to_string(), + script_path_arg, + payload_path_arg, + json, + ]) + .expect("build command"); + command.status().await.expect("run hook command"); + HookOutcome::Continue + }) + }), + }; + + let payload = hook_payload("4"); + let expected = to_string(&payload)?; + + let hooks = hooks_for_after_agent(vec![hook]); + hooks.dispatch(payload).await; + + let contents = timeout(Duration::from_secs(2), async { + loop { + if let Ok(contents) = fs::read_to_string(&payload_path) + && !contents.is_empty() + { + return contents; + } + tokio::time::sleep(Duration::from_millis(10)).await; + } + }) + .await?; + + assert_eq!(contents, expected); + Ok(()) + } +} diff --git a/codex-rs/core/src/hooks/types.rs b/codex-rs/core/src/hooks/types.rs new file mode 100644 index 0000000000..3b22d031b6 --- /dev/null +++ b/codex-rs/core/src/hooks/types.rs @@ -0,0 +1,127 @@ +use std::path::PathBuf; +use std::sync::Arc; + +use chrono::DateTime; +use chrono::SecondsFormat; +use chrono::Utc; +use codex_protocol::ThreadId; +use futures::future::BoxFuture; +use serde::Serialize; +use serde::Serializer; + +pub(crate) type HookFn = + Arc Fn(&'a HookPayload) -> BoxFuture<'a, HookOutcome> + Send + Sync>; + +#[derive(Clone)] +pub(crate) struct Hook { + pub(crate) func: HookFn, +} + +impl Default for Hook { + fn default() -> Self { + Self { + func: Arc::new(|_| Box::pin(async { HookOutcome::Continue })), + } + } +} + +impl Hook { + pub(super) async fn execute(&self, payload: &HookPayload) -> HookOutcome { + (self.func)(payload).await + } +} + +#[derive(Debug, Serialize, Clone)] +#[serde(rename_all = "snake_case")] +pub(crate) struct HookPayload { + pub(crate) session_id: ThreadId, + pub(crate) cwd: PathBuf, + #[serde(serialize_with = "serialize_triggered_at")] + pub(crate) triggered_at: DateTime, + pub(crate) hook_event: HookEvent, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "snake_case")] +pub(crate) struct HookEventAfterAgent { + pub thread_id: ThreadId, + pub turn_id: String, + pub input_messages: Vec, + pub last_assistant_message: Option, +} + +fn serialize_triggered_at(value: &DateTime, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&value.to_rfc3339_opts(SecondsFormat::Secs, true)) +} + +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "event_type", rename_all = "snake_case")] +pub(crate) enum HookEvent { + AfterAgent { + #[serde(flatten)] + event: HookEventAfterAgent, + }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum HookOutcome { + Continue, + #[allow(dead_code)] + Stop, +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use chrono::TimeZone; + use chrono::Utc; + use codex_protocol::ThreadId; + use pretty_assertions::assert_eq; + use serde_json::json; + + use super::HookEvent; + use super::HookEventAfterAgent; + use super::HookPayload; + + #[test] + fn hook_payload_serializes_stable_wire_shape() { + let session_id = ThreadId::new(); + let thread_id = ThreadId::new(); + let payload = HookPayload { + session_id, + cwd: PathBuf::from("tmp"), + triggered_at: Utc + .with_ymd_and_hms(2025, 1, 1, 0, 0, 0) + .single() + .expect("valid timestamp"), + hook_event: HookEvent::AfterAgent { + event: HookEventAfterAgent { + thread_id, + turn_id: "turn-1".to_string(), + input_messages: vec!["hello".to_string()], + last_assistant_message: Some("hi".to_string()), + }, + }, + }; + + let actual = serde_json::to_value(payload).expect("serialize hook payload"); + let expected = json!({ + "session_id": session_id.to_string(), + "cwd": "tmp", + "triggered_at": "2025-01-01T00:00:00Z", + "hook_event": { + "event_type": "after_agent", + "thread_id": thread_id.to_string(), + "turn_id": "turn-1", + "input_messages": ["hello"], + "last_assistant_message": "hi", + }, + }); + + assert_eq!(actual, expected); + } +} diff --git a/codex-rs/core/src/hooks/user_notification.rs b/codex-rs/core/src/hooks/user_notification.rs new file mode 100644 index 0000000000..de1317f9c3 --- /dev/null +++ b/codex-rs/core/src/hooks/user_notification.rs @@ -0,0 +1,132 @@ +use std::sync::Arc; + +use serde::Serialize; +use std::path::Path; +use std::process::Stdio; + +use super::registry::command_from_argv; +use super::types::Hook; +use super::types::HookEvent; +use super::types::HookOutcome; +use super::types::HookPayload; + +/// Legacy notify payload appended as the final argv argument for backward compatibility. +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(tag = "type", rename_all = "kebab-case")] +enum UserNotification { + #[serde(rename_all = "kebab-case")] + AgentTurnComplete { + thread_id: String, + turn_id: String, + cwd: String, + + /// Messages that the user sent to the agent to initiate the turn. + input_messages: Vec, + + /// The last message sent by the assistant in the turn. + last_assistant_message: Option, + }, +} + +pub(super) fn legacy_notify_json( + hook_event: &HookEvent, + cwd: &Path, +) -> Result { + serde_json::to_string(&match hook_event { + HookEvent::AfterAgent { event } => UserNotification::AgentTurnComplete { + thread_id: event.thread_id.to_string(), + turn_id: event.turn_id.clone(), + cwd: cwd.display().to_string(), + input_messages: event.input_messages.clone(), + last_assistant_message: event.last_assistant_message.clone(), + }, + }) +} + +pub(super) fn notify_hook(argv: Vec) -> Hook { + let argv = Arc::new(argv); + Hook { + func: Arc::new(move |payload: &HookPayload| { + let argv = Arc::clone(&argv); + Box::pin(async move { + let mut command = match command_from_argv(&argv) { + Some(command) => command, + None => return HookOutcome::Continue, + }; + if let Ok(notify_payload) = legacy_notify_json(&payload.hook_event, &payload.cwd) { + command.arg(notify_payload); + } + + // Backwards-compat: match legacy notify behavior (argv + JSON arg, fire-and-forget). + command + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()); + + let _ = command.spawn(); + HookOutcome::Continue + }) + }), + } +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + use super::*; + use anyhow::Result; + use codex_protocol::ThreadId; + use pretty_assertions::assert_eq; + use serde_json::Value; + use serde_json::json; + + fn expected_notification_json() -> Value { + json!({ + "type": "agent-turn-complete", + "thread-id": "b5f6c1c2-1111-2222-3333-444455556666", + "turn-id": "12345", + "cwd": "/Users/example/project", + "input-messages": ["Rename `foo` to `bar` and update the callsites."], + "last-assistant-message": "Rename complete and verified `cargo build` succeeds.", + }) + } + + #[test] + fn test_user_notification() -> Result<()> { + let notification = UserNotification::AgentTurnComplete { + thread_id: "b5f6c1c2-1111-2222-3333-444455556666".to_string(), + turn_id: "12345".to_string(), + cwd: "/Users/example/project".to_string(), + input_messages: vec!["Rename `foo` to `bar` and update the callsites.".to_string()], + last_assistant_message: Some( + "Rename complete and verified `cargo build` succeeds.".to_string(), + ), + }; + let serialized = serde_json::to_string(¬ification)?; + let actual: Value = serde_json::from_str(&serialized)?; + assert_eq!(actual, expected_notification_json()); + Ok(()) + } + + #[test] + fn legacy_notify_json_matches_historical_wire_shape() -> Result<()> { + let hook_event = HookEvent::AfterAgent { + event: super::super::types::HookEventAfterAgent { + thread_id: ThreadId::from_string("b5f6c1c2-1111-2222-3333-444455556666") + .expect("valid thread id"), + turn_id: "12345".to_string(), + input_messages: vec!["Rename `foo` to `bar` and update the callsites.".to_string()], + last_assistant_message: Some( + "Rename complete and verified `cargo build` succeeds.".to_string(), + ), + }, + }; + + let serialized = legacy_notify_json(&hook_event, Path::new("/Users/example/project"))?; + let actual: Value = serde_json::from_str(&serialized)?; + assert_eq!(actual, expected_notification_json()); + + Ok(()) + } +} diff --git a/codex-rs/core/src/landlock.rs b/codex-rs/core/src/landlock.rs index 340aebff2c..ea27f77f75 100644 --- a/codex-rs/core/src/landlock.rs +++ b/codex-rs/core/src/landlock.rs @@ -6,26 +6,34 @@ use std::path::Path; use std::path::PathBuf; use tokio::process::Child; -/// Spawn a shell tool command under the Linux Landlock+seccomp sandbox helper -/// (codex-linux-sandbox). +/// Spawn a shell tool command under the Linux sandbox helper +/// (codex-linux-sandbox), which currently uses bubblewrap for filesystem +/// isolation plus seccomp for network restrictions. /// /// Unlike macOS Seatbelt where we directly embed the policy text, the Linux /// helper accepts a list of `--sandbox-permission`/`-s` flags mirroring the /// public CLI. We convert the internal [`SandboxPolicy`] representation into /// the equivalent CLI options. +#[allow(clippy::too_many_arguments)] pub async fn spawn_command_under_linux_sandbox

( codex_linux_sandbox_exe: P, command: Vec, command_cwd: PathBuf, sandbox_policy: &SandboxPolicy, sandbox_policy_cwd: &Path, + use_bwrap_sandbox: bool, stdio_policy: StdioPolicy, env: HashMap, ) -> std::io::Result where P: AsRef, { - let args = create_linux_sandbox_command_args(command, sandbox_policy, sandbox_policy_cwd); + let args = create_linux_sandbox_command_args( + command, + sandbox_policy, + sandbox_policy_cwd, + use_bwrap_sandbox, + ); let arg0 = Some("codex-linux-sandbox"); spawn_child_async( codex_linux_sandbox_exe.as_ref().to_path_buf(), @@ -40,10 +48,14 @@ where } /// Converts the sandbox policy into the CLI invocation for `codex-linux-sandbox`. +/// +/// The helper performs the actual sandboxing (bubblewrap + seccomp) after +/// parsing these arguments. See `docs/linux_sandbox.md` for the Linux semantics. pub(crate) fn create_linux_sandbox_command_args( command: Vec, sandbox_policy: &SandboxPolicy, sandbox_policy_cwd: &Path, + use_bwrap_sandbox: bool, ) -> Vec { #[expect(clippy::expect_used)] let sandbox_policy_cwd = sandbox_policy_cwd @@ -60,13 +72,42 @@ pub(crate) fn create_linux_sandbox_command_args( sandbox_policy_cwd, "--sandbox-policy".to_string(), sandbox_policy_json, - // Separator so that command arguments starting with `-` are not parsed as - // options of the helper itself. - "--".to_string(), ]; + if use_bwrap_sandbox { + linux_cmd.push("--use-bwrap-sandbox".to_string()); + } + + // Separator so that command arguments starting with `-` are not parsed as + // options of the helper itself. + linux_cmd.push("--".to_string()); // Append the original tool command. linux_cmd.extend(command); linux_cmd } + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn bwrap_flags_are_feature_gated() { + let command = vec!["/bin/true".to_string()]; + let cwd = Path::new("/tmp"); + let policy = SandboxPolicy::ReadOnly; + + let with_bwrap = create_linux_sandbox_command_args(command.clone(), &policy, cwd, true); + assert_eq!( + with_bwrap.contains(&"--use-bwrap-sandbox".to_string()), + true + ); + + let without_bwrap = create_linux_sandbox_command_args(command, &policy, cwd, false); + assert_eq!( + without_bwrap.contains(&"--use-bwrap-sandbox".to_string()), + false + ); + } +} diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index 522eeb3210..302fa20b26 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -13,6 +13,7 @@ pub mod bash; mod client; mod client_common; pub mod codex; +pub use codex::SteerInputError; mod codex_thread; mod compact_remote; pub use codex_thread::CodexThread; @@ -32,14 +33,15 @@ pub mod exec; pub mod exec_env; mod exec_policy; pub mod features; +mod file_watcher; mod flags; pub mod git_info; +pub mod hooks; pub mod instructions; pub mod landlock; pub mod mcp; mod mcp_connection_manager; pub mod models_manager; -mod transport_manager; pub use mcp_connection_manager::MCP_SANDBOX_STATE_CAPABILITY; pub use mcp_connection_manager::MCP_SANDBOX_STATE_METHOD; pub use mcp_connection_manager::SandboxState; @@ -61,6 +63,7 @@ pub mod token_data; mod truncate; mod unified_exec; pub mod windows_sandbox; +pub use client::X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER; pub use model_provider_info::DEFAULT_LMSTUDIO_PORT; pub use model_provider_info::DEFAULT_OLLAMA_PORT; pub use model_provider_info::LMSTUDIO_OSS_PROVIDER_ID; @@ -110,6 +113,7 @@ pub use rollout::SessionMeta; pub use rollout::find_archived_thread_path_by_id_str; #[deprecated(note = "use find_thread_path_by_id_str")] pub use rollout::find_conversation_path_by_id_str; +pub use rollout::find_thread_name_by_id; pub use rollout::find_thread_path_by_id_str; pub use rollout::find_thread_path_by_name_str; pub use rollout::list::Cursor; @@ -121,24 +125,23 @@ pub use rollout::list::read_head_for_summary; pub use rollout::list::read_session_meta_line; pub use rollout::rollout_date_parts; pub use rollout::session_index::find_thread_names_by_ids; -pub use transport_manager::TransportManager; mod function_tool; mod state; mod tasks; -mod user_notification; mod user_shell_command; pub mod util; pub use apply_patch::CODEX_APPLY_PATCH_ARG1; -pub use client::WEB_SEARCH_ELIGIBLE_HEADER; pub use client::X_CODEX_TURN_METADATA_HEADER; pub use command_safety::is_dangerous_command; pub use command_safety::is_safe_command; pub use exec_policy::ExecPolicyError; pub use exec_policy::check_execpolicy_for_warnings; pub use exec_policy::load_exec_policy; +pub use file_watcher::FileWatcherEvent; pub use safety::get_platform_sandbox; pub use tools::spec::parse_tool_input_schema; +pub use turn_metadata::build_turn_metadata_header; // Re-export the protocol types from the standalone `codex-protocol` crate so existing // `codex_core::protocol::...` references continue to work across the workspace. pub use codex_protocol::protocol; @@ -160,4 +163,5 @@ pub use codex_protocol::models::ResponseItem; pub use compact::content_items_to_text; pub use event_mapping::parse_turn_item; pub mod compact; +pub mod memory_trace; pub mod otel_init; diff --git a/codex-rs/core/src/mcp/mod.rs b/codex-rs/core/src/mcp/mod.rs index cb0b5e2f26..970fa5db40 100644 --- a/codex-rs/core/src/mcp/mod.rs +++ b/codex-rs/core/src/mcp/mod.rs @@ -167,6 +167,7 @@ pub async fn collect_mcp_snapshot(config: &Config) -> McpListToolsResponseEvent sandbox_policy: SandboxPolicy::ReadOnly, codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(), sandbox_cwd: env::current_dir().unwrap_or_else(|_| PathBuf::from("/")), + use_linux_sandbox_bwrap: config.features.enabled(Feature::UseLinuxSandboxBwrap), }; mcp_connection_manager diff --git a/codex-rs/core/src/mcp/skill_dependencies.rs b/codex-rs/core/src/mcp/skill_dependencies.rs index 37620d73db..14b71fcf19 100644 --- a/codex-rs/core/src/mcp/skill_dependencies.rs +++ b/codex-rs/core/src/mcp/skill_dependencies.rs @@ -144,7 +144,7 @@ pub(crate) async fn maybe_prompt_and_install_mcp_dependencies( return; } - let config = turn_context.client.config(); + let config = turn_context.config.clone(); if mentioned_skills.is_empty() || !config.features.enabled(Feature::SkillMcpDependencyInstall) { return; } diff --git a/codex-rs/core/src/mcp_connection_manager.rs b/codex-rs/core/src/mcp_connection_manager.rs index c5fbb8ec3d..d6447738c2 100644 --- a/codex-rs/core/src/mcp_connection_manager.rs +++ b/codex-rs/core/src/mcp_connection_manager.rs @@ -12,7 +12,10 @@ use std::env; use std::ffi::OsString; use std::path::PathBuf; use std::sync::Arc; +use std::sync::LazyLock; +use std::sync::Mutex as StdMutex; use std::time::Duration; +use std::time::Instant; use crate::mcp::CODEX_APPS_MCP_SERVER_NAME; use crate::mcp::auth::McpAuthStatusEntry; @@ -83,6 +86,8 @@ pub const DEFAULT_STARTUP_TIMEOUT: Duration = Duration::from_secs(10); /// Default timeout for individual tool calls. const DEFAULT_TOOL_TIMEOUT: Duration = Duration::from_secs(60); +const CODEX_APPS_TOOLS_CACHE_TTL: Duration = Duration::from_secs(3600); + /// The Responses API requires tool names to match `^[a-zA-Z0-9_-]+$`. /// MCP server/tool names are user-controlled, so sanitize the fully-qualified /// name we expose to the model by replacing any disallowed character with `_`. @@ -161,6 +166,15 @@ pub(crate) struct ToolInfo { pub(crate) connector_name: Option, } +#[derive(Clone)] +struct CachedCodexAppsTools { + expires_at: Instant, + tools: Vec, +} + +static CODEX_APPS_TOOLS_CACHE: LazyLock>> = + LazyLock::new(|| StdMutex::new(None)); + type ResponderMap = HashMap<(String, RequestId), oneshot::Sender>; #[derive(Clone, Default)] @@ -313,6 +327,8 @@ pub struct SandboxState { pub sandbox_policy: SandboxPolicy, pub codex_linux_sandbox_exe: Option, pub sandbox_cwd: PathBuf, + #[serde(default)] + pub use_linux_sandbox_bwrap: bool, } /// A thin wrapper around a set of running [`RmcpClient`] instances. @@ -463,13 +479,28 @@ impl McpConnectionManager { #[instrument(level = "trace", skip_all)] pub async fn list_all_tools(&self) -> HashMap { let mut tools = HashMap::new(); - for managed_client in self.clients.values() { + for (server_name, managed_client) in &self.clients { let client = managed_client.client().await.ok(); if let Some(client) = client { - tools.extend(qualify_tools(filter_tools( - client.tools, - client.tool_filter, - ))); + let rmcp_client = client.client; + let tool_timeout = client.tool_timeout; + let tool_filter = client.tool_filter; + let mut server_tools = client.tools; + + if server_name == CODEX_APPS_MCP_SERVER_NAME { + match list_tools_for_client(server_name, &rmcp_client, tool_timeout).await { + Ok(fresh_or_cached_tools) => { + server_tools = fresh_or_cached_tools; + } + Err(err) => { + warn!( + "Failed to refresh tools for MCP server '{server_name}', using startup snapshot: {err:#}" + ); + } + } + } + + tools.extend(qualify_tools(filter_tools(server_tools, tool_filter))); } } tools @@ -963,6 +994,50 @@ async fn list_tools_for_client( server_name: &str, client: &Arc, timeout: Option, +) -> Result> { + if server_name == CODEX_APPS_MCP_SERVER_NAME + && let Some(cached_tools) = read_cached_codex_apps_tools() + { + return Ok(cached_tools); + } + + let tools = list_tools_for_client_uncached(server_name, client, timeout).await?; + if server_name == CODEX_APPS_MCP_SERVER_NAME { + write_cached_codex_apps_tools(&tools); + } + Ok(tools) +} + +fn read_cached_codex_apps_tools() -> Option> { + let mut cache_guard = CODEX_APPS_TOOLS_CACHE + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let now = Instant::now(); + + if let Some(cached) = cache_guard.as_ref() + && now < cached.expires_at + { + return Some(cached.tools.clone()); + } + + *cache_guard = None; + None +} + +fn write_cached_codex_apps_tools(tools: &[ToolInfo]) { + let mut cache_guard = CODEX_APPS_TOOLS_CACHE + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + *cache_guard = Some(CachedCodexAppsTools { + expires_at: Instant::now() + CODEX_APPS_TOOLS_CACHE_TTL, + tools: tools.to_vec(), + }); +} + +async fn list_tools_for_client_uncached( + server_name: &str, + client: &Arc, + timeout: Option, ) -> Result> { let resp = client.list_tools_with_connector_ids(None, timeout).await?; Ok(resp diff --git a/codex-rs/core/src/mcp_tool_call.rs b/codex-rs/core/src/mcp_tool_call.rs index 75248f34cc..737b130247 100644 --- a/codex-rs/core/src/mcp_tool_call.rs +++ b/codex-rs/core/src/mcp_tool_call.rs @@ -11,15 +11,18 @@ use crate::protocol::McpInvocation; use crate::protocol::McpToolCallBeginEvent; use crate::protocol::McpToolCallEndEvent; use codex_protocol::mcp::CallToolResult; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::ResponseInputItem; use codex_protocol::protocol::AskForApproval; +use codex_protocol::protocol::ReviewDecision; use codex_protocol::protocol::SandboxPolicy; use codex_protocol::request_user_input::RequestUserInputArgs; use codex_protocol::request_user_input::RequestUserInputQuestion; use codex_protocol::request_user_input::RequestUserInputQuestionOption; use codex_protocol::request_user_input::RequestUserInputResponse; use rmcp::model::ToolAnnotations; +use serde::Serialize; use std::sync::Arc; /// Handles the specified tool call dispatches the appropriate @@ -44,9 +47,8 @@ pub(crate) async fn handle_mcp_tool_call( return ResponseInputItem::FunctionCallOutput { call_id: call_id.clone(), output: FunctionCallOutputPayload { - content: format!("err: {e}"), + body: FunctionCallOutputBody::Text(format!("err: {e}")), success: Some(false), - ..Default::default() }, }; } @@ -64,7 +66,7 @@ pub(crate) async fn handle_mcp_tool_call( .await { let result = match decision { - McpToolApprovalDecision::Accept => { + McpToolApprovalDecision::Accept | McpToolApprovalDecision::AcceptAndRemember => { let tool_call_begin_event = EventMsg::McpToolCallBegin(McpToolCallBeginEvent { call_id: call_id.clone(), invocation: invocation.clone(), @@ -120,8 +122,7 @@ pub(crate) async fn handle_mcp_tool_call( let status = if result.is_ok() { "ok" } else { "error" }; turn_context - .client - .get_otel_manager() + .otel_manager .counter("codex.mcp.call", 1, &[("status", status)]); return ResponseInputItem::McpToolCallOutput { call_id, result }; @@ -153,8 +154,7 @@ pub(crate) async fn handle_mcp_tool_call( let status = if result.is_ok() { "ok" } else { "error" }; turn_context - .client - .get_otel_manager() + .otel_manager .counter("codex.mcp.call", 1, &[("status", status)]); ResponseInputItem::McpToolCallOutput { call_id, result } @@ -167,21 +167,31 @@ async fn notify_mcp_tool_call_event(sess: &Session, turn_context: &TurnContext, #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum McpToolApprovalDecision { Accept, + AcceptAndRemember, Decline, Cancel, } struct McpToolApprovalMetadata { annotations: ToolAnnotations, + connector_id: Option, connector_name: Option, tool_title: Option, } const MCP_TOOL_APPROVAL_QUESTION_ID_PREFIX: &str = "mcp_tool_call_approval"; -const MCP_TOOL_APPROVAL_ACCEPT: &str = "Accept"; -const MCP_TOOL_APPROVAL_DECLINE: &str = "Decline"; +const MCP_TOOL_APPROVAL_ACCEPT: &str = "Approve Once"; +const MCP_TOOL_APPROVAL_ACCEPT_AND_REMEMBER: &str = "Approve this Session"; +const MCP_TOOL_APPROVAL_DECLINE: &str = "Deny"; const MCP_TOOL_APPROVAL_CANCEL: &str = "Cancel"; +#[derive(Debug, Serialize)] +struct McpToolApprovalKey { + server: String, + connector_id: String, + tool_name: String, +} + async fn maybe_request_mcp_tool_approval( sess: &Session, turn_context: &TurnContext, @@ -200,6 +210,19 @@ async fn maybe_request_mcp_tool_approval( if !requires_mcp_tool_approval(&metadata.annotations) { return None; } + let approval_key = metadata + .connector_id + .as_deref() + .map(|connector_id| McpToolApprovalKey { + server: server.to_string(), + connector_id: connector_id.to_string(), + tool_name: tool_name.to_string(), + }); + if let Some(key) = approval_key.as_ref() + && mcp_tool_approval_is_remembered(sess, key).await + { + return Some(McpToolApprovalDecision::Accept); + } let question_id = format!("{MCP_TOOL_APPROVAL_QUESTION_ID_PREFIX}_{call_id}"); let question = build_mcp_tool_approval_question( @@ -208,6 +231,7 @@ async fn maybe_request_mcp_tool_approval( metadata.tool_title.as_deref(), metadata.connector_name.as_deref(), &metadata.annotations, + approval_key.is_some(), ); let args = RequestUserInputArgs { questions: vec![question], @@ -215,7 +239,13 @@ async fn maybe_request_mcp_tool_approval( let response = sess .request_user_input(turn_context, call_id.to_string(), args) .await; - Some(parse_mcp_tool_approval_response(response, &question_id)) + let decision = parse_mcp_tool_approval_response(response, &question_id); + if matches!(decision, McpToolApprovalDecision::AcceptAndRemember) + && let Some(key) = approval_key + { + remember_mcp_tool_approval(sess, key).await; + } + Some(decision) } fn is_full_access_mode(turn_context: &TurnContext) -> bool { @@ -246,6 +276,7 @@ async fn lookup_mcp_tool_metadata( .annotations .map(|annotations| McpToolApprovalMetadata { annotations, + connector_id: tool_info.connector_id, connector_name: tool_info.connector_name, tool_title: tool_info.tool.title, }) @@ -261,6 +292,7 @@ fn build_mcp_tool_approval_question( tool_title: Option<&str>, connector_name: Option<&str>, annotations: &ToolAnnotations, + allow_remember_option: bool, ) -> RequestUserInputQuestion { let destructive = annotations.destructive_hint == Some(true); let open_world = annotations.open_world_hint == Some(true); @@ -279,26 +311,34 @@ fn build_mcp_tool_approval_question( "{app_label} wants to run the tool \"{tool_label}\", which {reason}. Allow this action?" ); + let mut options = vec![RequestUserInputQuestionOption { + label: MCP_TOOL_APPROVAL_ACCEPT.to_string(), + description: "Run the tool and continue.".to_string(), + }]; + if allow_remember_option { + options.push(RequestUserInputQuestionOption { + label: MCP_TOOL_APPROVAL_ACCEPT_AND_REMEMBER.to_string(), + description: "Run the tool and remember this choice for this session.".to_string(), + }); + } + options.extend([ + RequestUserInputQuestionOption { + label: MCP_TOOL_APPROVAL_DECLINE.to_string(), + description: "Decline this tool call and continue.".to_string(), + }, + RequestUserInputQuestionOption { + label: MCP_TOOL_APPROVAL_CANCEL.to_string(), + description: "Cancel this tool call".to_string(), + }, + ]); + RequestUserInputQuestion { id: question_id, header: "Approve app tool call?".to_string(), question, is_other: false, is_secret: false, - options: Some(vec![ - RequestUserInputQuestionOption { - label: MCP_TOOL_APPROVAL_ACCEPT.to_string(), - description: "Run the tool and continue.".to_string(), - }, - RequestUserInputQuestionOption { - label: MCP_TOOL_APPROVAL_DECLINE.to_string(), - description: "Decline this tool call and continue.".to_string(), - }, - RequestUserInputQuestionOption { - label: MCP_TOOL_APPROVAL_CANCEL.to_string(), - description: "Cancel this tool call".to_string(), - }, - ]), + options: Some(options), } } @@ -317,6 +357,11 @@ fn parse_mcp_tool_approval_response( return McpToolApprovalDecision::Cancel; }; if answers + .iter() + .any(|answer| answer == MCP_TOOL_APPROVAL_ACCEPT_AND_REMEMBER) + { + McpToolApprovalDecision::AcceptAndRemember + } else if answers .iter() .any(|answer| answer == MCP_TOOL_APPROVAL_ACCEPT) { @@ -331,6 +376,16 @@ fn parse_mcp_tool_approval_response( } } +async fn mcp_tool_approval_is_remembered(sess: &Session, key: &McpToolApprovalKey) -> bool { + let store = sess.services.tool_approvals.lock().await; + matches!(store.get(key), Some(ReviewDecision::ApprovedForSession)) +} + +async fn remember_mcp_tool_approval(sess: &Session, key: McpToolApprovalKey) { + let mut store = sess.services.tool_approvals.lock().await; + store.put(key, ReviewDecision::ApprovedForSession); +} + fn requires_mcp_tool_approval(annotations: &ToolAnnotations) -> bool { annotations.read_only_hint == Some(false) && (annotations.destructive_hint == Some(true) || annotations.open_world_hint == Some(true)) diff --git a/codex-rs/core/src/memory_trace.rs b/codex-rs/core/src/memory_trace.rs new file mode 100644 index 0000000000..6519980143 --- /dev/null +++ b/codex-rs/core/src/memory_trace.rs @@ -0,0 +1,303 @@ +use std::path::Path; +use std::path::PathBuf; + +use crate::ModelClient; +use crate::error::CodexErr; +use crate::error::Result; +use codex_api::MemoryTrace as ApiMemoryTrace; +use codex_api::MemoryTraceMetadata as ApiMemoryTraceMetadata; +use codex_otel::OtelManager; +use codex_protocol::openai_models::ModelInfo; +use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; +use serde_json::Map; +use serde_json::Value; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BuiltTraceMemory { + pub trace_id: String, + pub source_path: PathBuf, + pub trace_summary: String, + pub memory_summary: String, +} + +struct PreparedTrace { + trace_id: String, + source_path: PathBuf, + payload: ApiMemoryTrace, +} + +/// Loads raw trace files, normalizes trace items, and builds memory summaries. +/// +/// The request/response wiring mirrors the memory trace summarize E2E flow: +/// `/v1/memories/trace_summarize` with one output object per input trace. +/// +/// The caller provides the model selection, reasoning effort, and telemetry context explicitly so +/// the session-scoped [`ModelClient`] can be reused across turns. +pub async fn build_memories_from_trace_files( + client: &ModelClient, + trace_paths: &[PathBuf], + model_info: &ModelInfo, + effort: Option, + otel_manager: &OtelManager, +) -> Result> { + if trace_paths.is_empty() { + return Ok(Vec::new()); + } + + let mut prepared = Vec::with_capacity(trace_paths.len()); + for (index, path) in trace_paths.iter().enumerate() { + prepared.push(prepare_trace(index + 1, path).await?); + } + + let traces = prepared.iter().map(|trace| trace.payload.clone()).collect(); + let output = client + .summarize_memory_traces(traces, model_info, effort, otel_manager) + .await?; + if output.len() != prepared.len() { + return Err(CodexErr::InvalidRequest(format!( + "unexpected memory summarize output length: expected {}, got {}", + prepared.len(), + output.len() + ))); + } + + Ok(prepared + .into_iter() + .zip(output) + .map(|(trace, summary)| BuiltTraceMemory { + trace_id: trace.trace_id, + source_path: trace.source_path, + trace_summary: summary.trace_summary, + memory_summary: summary.memory_summary, + }) + .collect()) +} + +async fn prepare_trace(index: usize, path: &Path) -> Result { + let text = load_trace_text(path).await?; + let items = load_trace_items(path, &text)?; + let trace_id = build_trace_id(index, path); + let source_path = path.to_path_buf(); + + Ok(PreparedTrace { + trace_id: trace_id.clone(), + source_path: source_path.clone(), + payload: ApiMemoryTrace { + id: trace_id, + metadata: ApiMemoryTraceMetadata { + source_path: source_path.display().to_string(), + }, + items, + }, + }) +} + +async fn load_trace_text(path: &Path) -> Result { + let raw = tokio::fs::read(path).await?; + Ok(decode_trace_bytes(&raw)) +} + +fn decode_trace_bytes(raw: &[u8]) -> String { + if let Some(without_bom) = raw.strip_prefix(&[0xEF, 0xBB, 0xBF]) + && let Ok(text) = String::from_utf8(without_bom.to_vec()) + { + return text; + } + if let Ok(text) = String::from_utf8(raw.to_vec()) { + return text; + } + raw.iter().map(|b| char::from(*b)).collect() +} + +fn load_trace_items(path: &Path, text: &str) -> Result> { + if let Ok(Value::Array(items)) = serde_json::from_str::(text) { + let dict_items = items + .into_iter() + .filter(serde_json::Value::is_object) + .collect::>(); + if dict_items.is_empty() { + return Err(CodexErr::InvalidRequest(format!( + "no object items found in trace file: {}", + path.display() + ))); + } + return normalize_trace_items(dict_items, path); + } + + let mut parsed_items = Vec::new(); + for line in text.lines() { + let line = line.trim(); + if line.is_empty() || (!line.starts_with('{') && !line.starts_with('[')) { + continue; + } + + let Ok(obj) = serde_json::from_str::(line) else { + continue; + }; + + match obj { + Value::Object(_) => parsed_items.push(obj), + Value::Array(inner) => { + parsed_items.extend(inner.into_iter().filter(serde_json::Value::is_object)) + } + _ => {} + } + } + + if parsed_items.is_empty() { + return Err(CodexErr::InvalidRequest(format!( + "no JSON items parsed from trace file: {}", + path.display() + ))); + } + + normalize_trace_items(parsed_items, path) +} + +fn normalize_trace_items(items: Vec, path: &Path) -> Result> { + let mut normalized = Vec::new(); + + for item in items { + let Value::Object(obj) = item else { + continue; + }; + + if let Some(payload) = obj.get("payload") { + if obj.get("type").and_then(Value::as_str) != Some("response_item") { + continue; + } + + match payload { + Value::Object(payload_item) => { + if is_allowed_trace_item(payload_item) { + normalized.push(Value::Object(payload_item.clone())); + } + } + Value::Array(payload_items) => { + for payload_item in payload_items { + if let Value::Object(payload_item) = payload_item + && is_allowed_trace_item(payload_item) + { + normalized.push(Value::Object(payload_item.clone())); + } + } + } + _ => {} + } + continue; + } + + if is_allowed_trace_item(&obj) { + normalized.push(Value::Object(obj)); + } + } + + if normalized.is_empty() { + return Err(CodexErr::InvalidRequest(format!( + "no valid trace items after normalization: {}", + path.display() + ))); + } + Ok(normalized) +} + +fn is_allowed_trace_item(item: &Map) -> bool { + let Some(item_type) = item.get("type").and_then(Value::as_str) else { + return false; + }; + + if item_type == "message" { + return matches!( + item.get("role").and_then(Value::as_str), + Some("assistant" | "system" | "developer" | "user") + ); + } + + true +} + +fn build_trace_id(index: usize, path: &Path) -> String { + let stem = path + .file_stem() + .map(|stem| stem.to_string_lossy().into_owned()) + .filter(|stem| !stem.is_empty()) + .unwrap_or_else(|| "trace".to_string()); + format!("trace_{index}_{stem}") +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + use tempfile::tempdir; + + #[test] + fn normalize_trace_items_handles_payload_wrapper_and_message_role_filtering() { + let items = vec![ + serde_json::json!({ + "type": "response_item", + "payload": {"type": "message", "role": "assistant", "content": []} + }), + serde_json::json!({ + "type": "response_item", + "payload": [ + {"type": "message", "role": "user", "content": []}, + {"type": "message", "role": "tool", "content": []}, + {"type": "function_call", "name": "shell", "arguments": "{}", "call_id": "c1"} + ] + }), + serde_json::json!({ + "type": "not_response_item", + "payload": {"type": "message", "role": "assistant", "content": []} + }), + serde_json::json!({ + "type": "message", + "role": "developer", + "content": [] + }), + ]; + + let normalized = normalize_trace_items(items, Path::new("trace.json")).expect("normalize"); + let expected = vec![ + serde_json::json!({"type": "message", "role": "assistant", "content": []}), + serde_json::json!({"type": "message", "role": "user", "content": []}), + serde_json::json!({"type": "function_call", "name": "shell", "arguments": "{}", "call_id": "c1"}), + serde_json::json!({"type": "message", "role": "developer", "content": []}), + ]; + assert_eq!(normalized, expected); + } + + #[test] + fn load_trace_items_supports_jsonl_arrays_and_objects() { + let text = r#" +{"type":"response_item","payload":{"type":"message","role":"assistant","content":[]}} +[{"type":"message","role":"user","content":[]},{"type":"message","role":"tool","content":[]}] +"#; + let loaded = load_trace_items(Path::new("trace.jsonl"), text).expect("load"); + let expected = vec![ + serde_json::json!({"type":"message","role":"assistant","content":[]}), + serde_json::json!({"type":"message","role":"user","content":[]}), + ]; + assert_eq!(loaded, expected); + } + + #[tokio::test] + async fn load_trace_text_decodes_utf8_sig() { + let dir = tempdir().expect("tempdir"); + let path = dir.path().join("trace.json"); + tokio::fs::write( + &path, + [ + 0xEF, 0xBB, 0xBF, b'[', b'{', b'"', b't', b'y', b'p', b'e', b'"', b':', b'"', b'm', + b'e', b's', b's', b'a', b'g', b'e', b'"', b',', b'"', b'r', b'o', b'l', b'e', b'"', + b':', b'"', b'u', b's', b'e', b'r', b'"', b',', b'"', b'c', b'o', b'n', b't', b'e', + b'n', b't', b'"', b':', b'[', b']', b'}', b']', + ], + ) + .await + .expect("write"); + + let text = load_trace_text(&path).await.expect("decode"); + assert!(text.starts_with('[')); + } +} diff --git a/codex-rs/core/src/model_provider_info.rs b/codex-rs/core/src/model_provider_info.rs index 211822f48f..305233ae7a 100644 --- a/codex-rs/core/src/model_provider_info.rs +++ b/codex-rs/core/src/model_provider_info.rs @@ -8,7 +8,6 @@ use crate::auth::AuthMode; use crate::error::EnvVarError; use codex_api::Provider as ApiProvider; -use codex_api::is_azure_responses_wire_base_url; use codex_api::provider::RetryConfig as ApiRetryConfig; use http::HeaderMap; use http::header::HeaderName; @@ -174,10 +173,6 @@ impl ModelProviderInfo { }) } - pub(crate) fn is_azure_responses_endpoint(&self) -> bool { - is_azure_responses_wire_base_url(&self.name, self.base_url.as_deref()) - } - /// If `env_key` is Some, returns the API key for this provider if present /// (and non-empty) in the environment. If `env_key` is required but /// cannot be found, returns an error. diff --git a/codex-rs/core/src/models_manager/collaboration_mode_presets.rs b/codex-rs/core/src/models_manager/collaboration_mode_presets.rs index 5f297b2cfa..cdc5134038 100644 --- a/codex-rs/core/src/models_manager/collaboration_mode_presets.rs +++ b/codex-rs/core/src/models_manager/collaboration_mode_presets.rs @@ -1,10 +1,13 @@ use codex_protocol::config_types::CollaborationModeMask; use codex_protocol::config_types::ModeKind; +use codex_protocol::config_types::TUI_VISIBLE_COLLABORATION_MODES; use codex_protocol::openai_models::ReasoningEffort; const COLLABORATION_MODE_PLAN: &str = include_str!("../../templates/collaboration_mode/plan.md"); const COLLABORATION_MODE_DEFAULT: &str = include_str!("../../templates/collaboration_mode/default.md"); +const KNOWN_MODE_NAMES_PLACEHOLDER: &str = "{{KNOWN_MODE_NAMES}}"; +const REQUEST_USER_INPUT_AVAILABILITY_PLACEHOLDER: &str = "{{REQUEST_USER_INPUT_AVAILABILITY}}"; pub(super) fn builtin_collaboration_mode_presets() -> Vec { vec![plan_preset(), default_preset()] @@ -17,7 +20,7 @@ pub fn test_builtin_collaboration_mode_presets() -> Vec { fn plan_preset() -> CollaborationModeMask { CollaborationModeMask { - name: "Plan".to_string(), + name: ModeKind::Plan.display_name().to_string(), mode: Some(ModeKind::Plan), model: None, reasoning_effort: Some(Some(ReasoningEffort::Medium)), @@ -27,10 +30,74 @@ fn plan_preset() -> CollaborationModeMask { fn default_preset() -> CollaborationModeMask { CollaborationModeMask { - name: "Default".to_string(), + name: ModeKind::Default.display_name().to_string(), mode: Some(ModeKind::Default), model: None, reasoning_effort: None, - developer_instructions: Some(Some(COLLABORATION_MODE_DEFAULT.to_string())), + developer_instructions: Some(Some(default_mode_instructions())), + } +} + +fn default_mode_instructions() -> String { + let known_mode_names = format_mode_names(&TUI_VISIBLE_COLLABORATION_MODES); + let request_user_input_availability = + request_user_input_availability_message(ModeKind::Default); + COLLABORATION_MODE_DEFAULT + .replace(KNOWN_MODE_NAMES_PLACEHOLDER, &known_mode_names) + .replace( + REQUEST_USER_INPUT_AVAILABILITY_PLACEHOLDER, + &request_user_input_availability, + ) +} + +fn format_mode_names(modes: &[ModeKind]) -> String { + let mode_names: Vec<&str> = modes.iter().map(|mode| mode.display_name()).collect(); + match mode_names.as_slice() { + [] => "none".to_string(), + [mode_name] => (*mode_name).to_string(), + [first, second] => format!("{first} and {second}"), + [..] => mode_names.join(", "), + } +} + +fn request_user_input_availability_message(mode: ModeKind) -> String { + let mode_name = mode.display_name(); + if mode.allows_request_user_input() { + format!("The `request_user_input` tool is available in {mode_name} mode.") + } else { + format!( + "The `request_user_input` tool is unavailable in {mode_name} mode. If you call it while in {mode_name} mode, it will return an error." + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn preset_names_use_mode_display_names() { + assert_eq!(plan_preset().name, ModeKind::Plan.display_name()); + assert_eq!(default_preset().name, ModeKind::Default.display_name()); + } + + #[test] + fn default_mode_instructions_replace_mode_names_placeholder() { + let default_instructions = default_preset() + .developer_instructions + .expect("default preset should include instructions") + .expect("default instructions should be set"); + + assert!(!default_instructions.contains(KNOWN_MODE_NAMES_PLACEHOLDER)); + assert!(!default_instructions.contains(REQUEST_USER_INPUT_AVAILABILITY_PLACEHOLDER)); + + let known_mode_names = format_mode_names(&TUI_VISIBLE_COLLABORATION_MODES); + let expected_snippet = format!("Known mode names are {known_mode_names}."); + assert!(default_instructions.contains(&expected_snippet)); + + let expected_availability_message = + request_user_input_availability_message(ModeKind::Default); + assert!(default_instructions.contains(&expected_availability_message)); } } diff --git a/codex-rs/core/src/models_manager/manager.rs b/codex-rs/core/src/models_manager/manager.rs index fffc280736..2d85185076 100644 --- a/codex-rs/core/src/models_manager/manager.rs +++ b/codex-rs/core/src/models_manager/manager.rs @@ -61,7 +61,7 @@ impl ModelsManager { let cache_path = codex_home.join(MODEL_CACHE_FILE); let cache_manager = ModelsCacheManager::new(cache_path, DEFAULT_MODEL_CACHE_TTL); Self { - local_models: builtin_model_presets(auth_manager.get_internal_auth_mode()), + local_models: builtin_model_presets(auth_manager.auth_mode()), remote_models: RwLock::new(Self::load_remote_models_from_file().unwrap_or_default()), auth_manager, etag: RwLock::new(None), @@ -175,7 +175,7 @@ impl ModelsManager { refresh_strategy: RefreshStrategy, ) -> CoreResult<()> { if !config.features.enabled(Feature::RemoteModels) - || self.auth_manager.get_internal_auth_mode() == Some(AuthMode::ApiKey) + || self.auth_manager.auth_mode() == Some(AuthMode::ApiKey) { return Ok(()); } @@ -204,7 +204,7 @@ impl ModelsManager { let _timer = codex_otel::start_global_timer("codex.remote_models.fetch_update.duration_ms", &[]); let auth = self.auth_manager.auth().await; - let auth_mode = self.auth_manager.get_internal_auth_mode(); + let auth_mode = self.auth_manager.auth_mode(); let api_provider = self.provider.to_api_provider(auth_mode)?; let api_auth = auth_provider_from_auth(auth.clone(), &self.provider)?; let transport = ReqwestTransport::new(build_reqwest_client()); @@ -275,10 +275,7 @@ impl ModelsManager { let remote_presets: Vec = remote_models.into_iter().map(Into::into).collect(); let existing_presets = self.local_models.clone(); let mut merged_presets = ModelPreset::merge(remote_presets, existing_presets); - let chatgpt_mode = matches!( - self.auth_manager.get_internal_auth_mode(), - Some(AuthMode::Chatgpt) - ); + let chatgpt_mode = matches!(self.auth_manager.auth_mode(), Some(AuthMode::Chatgpt)); merged_presets = ModelPreset::filter_by_auth(merged_presets, chatgpt_mode); for preset in &mut merged_presets { @@ -322,7 +319,7 @@ impl ModelsManager { let cache_path = codex_home.join(MODEL_CACHE_FILE); let cache_manager = ModelsCacheManager::new(cache_path, DEFAULT_MODEL_CACHE_TTL); Self { - local_models: builtin_model_presets(auth_manager.get_internal_auth_mode()), + local_models: builtin_model_presets(auth_manager.auth_mode()), remote_models: RwLock::new(Self::load_remote_models_from_file().unwrap_or_default()), auth_manager, etag: RwLock::new(None), diff --git a/codex-rs/core/src/rollout/list.rs b/codex-rs/core/src/rollout/list.rs index 3f057649a6..940402902e 100644 --- a/codex-rs/core/src/rollout/list.rs +++ b/codex-rs/core/src/rollout/list.rs @@ -15,9 +15,7 @@ use uuid::Uuid; use super::ARCHIVED_SESSIONS_SUBDIR; use super::SESSIONS_SUBDIR; -use crate::instructions::UserInstructions; use crate::protocol::EventMsg; -use crate::session_prefix::is_session_prefix_content; use crate::state_db; use codex_file_search as file_search; use codex_protocol::ThreadId; @@ -25,6 +23,7 @@ use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::RolloutLine; use codex_protocol::protocol::SessionMetaLine; use codex_protocol::protocol::SessionSource; +use codex_protocol::protocol::USER_MESSAGE_BEGIN; /// Returned page of thread (thread) summaries. #[derive(Debug, Default, PartialEq)] @@ -44,8 +43,24 @@ pub struct ThreadsPage { pub struct ThreadItem { /// Absolute path to the rollout file. pub path: PathBuf, - /// First up to `HEAD_RECORD_LIMIT` JSONL records parsed as JSON (includes meta line). - pub head: Vec, + /// Thread ID from session metadata. + pub thread_id: Option, + /// First user message captured for this thread, if any. + pub first_user_message: Option, + /// Working directory from session metadata. + pub cwd: Option, + /// Git branch from session metadata. + pub git_branch: Option, + /// Git commit SHA from session metadata. + pub git_sha: Option, + /// Git origin URL from session metadata. + pub git_origin_url: Option, + /// Session source from session metadata. + pub source: Option, + /// Model provider from session metadata. + pub model_provider: Option, + /// CLI version from session metadata. + pub cli_version: Option, /// RFC3339 timestamp string for when the session was created, if available. /// created_at comes from the filename timestamp with second precision. pub created_at: Option, @@ -63,11 +78,17 @@ pub type ConversationsPage = ThreadsPage; #[derive(Default)] struct HeadTailSummary { - head: Vec, saw_session_meta: bool, saw_user_event: bool, + thread_id: Option, + first_user_message: Option, + cwd: Option, + git_branch: Option, + git_sha: Option, + git_origin_url: Option, source: Option, model_provider: Option, + cli_version: Option, created_at: Option, updated_at: Option, } @@ -261,6 +282,14 @@ impl<'de> serde::Deserialize<'de> for Cursor { } } +impl From for Cursor { + fn from(anchor: codex_state::Anchor) -> Self { + let ts = OffsetDateTime::from_unix_timestamp(anchor.ts.timestamp()) + .unwrap_or(OffsetDateTime::UNIX_EPOCH); + Self::new(ts, anchor.id) + } +} + /// Retrieve recorded thread file paths with token pagination. The returned `next_cursor` /// can be supplied on the next call to resume after the last returned item, resilient to /// concurrent new sessions being appended. Ordering is stable by the requested sort key @@ -666,7 +695,8 @@ async fn build_thread_item( if !allowed_sources.is_empty() && !summary .source - .is_some_and(|source| allowed_sources.contains(&source)) + .as_ref() + .is_some_and(|source| allowed_sources.contains(source)) { return None; } @@ -678,7 +708,15 @@ async fn build_thread_item( // Apply filters: must have session meta and at least one user message event if summary.saw_session_meta && summary.saw_user_event { let HeadTailSummary { - head, + thread_id, + first_user_message, + cwd, + git_branch, + git_sha, + git_origin_url, + source, + model_provider, + cli_version, created_at, updated_at: mut summary_updated_at, .. @@ -688,7 +726,15 @@ async fn build_thread_item( } return Some(ThreadItem { path, - head, + thread_id, + first_user_message, + cwd, + git_branch, + git_sha, + git_origin_url, + source, + model_provider, + cli_version, created_at, updated_at: summary_updated_at, }); @@ -971,32 +1017,29 @@ async fn read_head_summary(path: &Path, head_limit: usize) -> io::Result { summary.source = Some(session_meta_line.meta.source.clone()); summary.model_provider = session_meta_line.meta.model_provider.clone(); + summary.thread_id = Some(session_meta_line.meta.id); + summary.cwd = Some(session_meta_line.meta.cwd.clone()); + summary.git_branch = session_meta_line + .git + .as_ref() + .and_then(|git| git.branch.clone()); + summary.git_sha = session_meta_line + .git + .as_ref() + .and_then(|git| git.commit_hash.clone()); + summary.git_origin_url = session_meta_line + .git + .as_ref() + .and_then(|git| git.repository_url.clone()); + summary.cli_version = Some(session_meta_line.meta.cli_version); summary.created_at = Some(session_meta_line.meta.timestamp.clone()); summary.saw_session_meta = true; - if summary.head.len() < head_limit - && let Ok(val) = serde_json::to_value(session_meta_line) - { - summary.head.push(val); - } } - RolloutItem::ResponseItem(item) => { + RolloutItem::ResponseItem(_) => { summary.created_at = summary .created_at .clone() .or_else(|| Some(rollout_line.timestamp.clone())); - if let codex_protocol::models::ResponseItem::Message { role, content, .. } = &item - && role == "user" - && !UserInstructions::is_user_instructions(content.as_slice()) - && !is_session_prefix_content(content.as_slice()) - { - tracing::warn!("Item: {item:#?}"); - summary.saw_user_event = true; - } - if summary.head.len() < head_limit - && let Ok(val) = serde_json::to_value(item) - { - summary.head.push(val); - } } RolloutItem::TurnContext(_) => { // Not included in `head`; skip. @@ -1005,8 +1048,14 @@ async fn read_head_summary(path: &Path, head_limit: usize) -> io::Result { - if matches!(ev, EventMsg::UserMessage(_)) { + if let EventMsg::UserMessage(user) = ev { summary.saw_user_event = true; + if summary.first_user_message.is_none() { + let message = strip_user_message_prefix(user.message.as_str()).to_string(); + if !message.is_empty() { + summary.first_user_message = Some(message); + } + } } } } @@ -1022,8 +1071,48 @@ async fn read_head_summary(path: &Path, head_limit: usize) -> io::Result io::Result> { - let summary = read_head_summary(path, HEAD_RECORD_LIMIT).await?; - Ok(summary.head) + use tokio::io::AsyncBufReadExt; + + let file = tokio::fs::File::open(path).await?; + let reader = tokio::io::BufReader::new(file); + let mut lines = reader.lines(); + let mut head = Vec::new(); + + while head.len() < HEAD_RECORD_LIMIT { + let Some(line) = lines.next_line().await? else { + break; + }; + let trimmed = line.trim(); + if trimmed.is_empty() { + continue; + } + if let Ok(rollout_line) = serde_json::from_str::(trimmed) { + match rollout_line.item { + RolloutItem::SessionMeta(session_meta_line) => { + if let Ok(value) = serde_json::to_value(session_meta_line) { + head.push(value); + } + } + RolloutItem::ResponseItem(item) => { + if let Ok(value) = serde_json::to_value(item) { + head.push(value); + } + } + RolloutItem::Compacted(_) + | RolloutItem::TurnContext(_) + | RolloutItem::EventMsg(_) => {} + } + } + } + + Ok(head) +} + +fn strip_user_message_prefix(text: &str) -> &str { + match text.find(USER_MESSAGE_BEGIN) { + Some(idx) => text[idx + USER_MESSAGE_BEGIN.len()..].trim(), + None => text.trim(), + } } /// Read the SessionMetaLine from the head of a rollout file for reuse by @@ -1081,20 +1170,26 @@ async fn find_thread_path_by_id_str_in_subdir( ARCHIVED_SESSIONS_SUBDIR => Some(true), _ => None, }; + let thread_id = ThreadId::from_string(id_str).ok(); let state_db_ctx = state_db::open_if_present(codex_home, "").await; if let Some(state_db_ctx) = state_db_ctx.as_deref() - && let Ok(thread_id) = ThreadId::from_string(id_str) - { - let db_path = state_db::find_rollout_path_by_id( + && let Some(thread_id) = thread_id + && let Some(db_path) = state_db::find_rollout_path_by_id( Some(state_db_ctx), thread_id, archived_only, "find_path_query", ) - .await; - if db_path.is_some() { - return Ok(db_path); + .await + { + if tokio::fs::try_exists(&db_path).await.unwrap_or(false) { + return Ok(Some(db_path)); } + tracing::error!( + "state db returned stale rollout path for thread {id_str}: {}", + db_path.display() + ); + state_db::record_discrepancy("find_thread_path_by_id_str_in_subdir", "stale_db_path"); } let mut root = codex_home.to_path_buf(); @@ -1116,9 +1211,16 @@ async fn find_thread_path_by_id_str_in_subdir( .map_err(|e| io::Error::other(format!("file search failed: {e}")))?; let found = results.matches.into_iter().next().map(|m| m.full_path()); - if found.is_some() { + if let Some(found_path) = found.as_ref() { tracing::error!("state db missing rollout path for thread {id_str}"); - state_db::record_discrepancy("find_thread_path_by_id_str_in_subdir", "path_mismatch"); + state_db::record_discrepancy("find_thread_path_by_id_str_in_subdir", "falling_back"); + state_db::read_repair_rollout_path( + state_db_ctx.as_deref(), + thread_id, + archived_only, + found_path.as_path(), + ) + .await; } Ok(found) diff --git a/codex-rs/core/src/rollout/metadata.rs b/codex-rs/core/src/rollout/metadata.rs index 42e52f78d6..dc87c74da4 100644 --- a/codex-rs/core/src/rollout/metadata.rs +++ b/codex-rs/core/src/rollout/metadata.rs @@ -13,14 +13,15 @@ use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::SandboxPolicy; use codex_protocol::protocol::SessionMetaLine; use codex_protocol::protocol::SessionSource; +use codex_state::BackfillState; use codex_state::BackfillStats; +use codex_state::BackfillStatus; use codex_state::DB_ERROR_METRIC; use codex_state::DB_METRIC_BACKFILL; use codex_state::DB_METRIC_BACKFILL_DURATION_MS; use codex_state::ExtractionOutcome; use codex_state::ThreadMetadataBuilder; use codex_state::apply_rollout_item; -use std::cmp::Reverse; use std::path::Path; use std::path::PathBuf; use tracing::info; @@ -28,6 +29,7 @@ use tracing::warn; const ROLLOUT_PREFIX: &str = "rollout-"; const ROLLOUT_SUFFIX: &str = ".jsonl"; +const BACKFILL_BATCH_SIZE: usize = 200; pub(crate) fn builder_from_session_meta( session_meta: &SessionMetaLine, @@ -42,6 +44,7 @@ pub(crate) fn builder_from_session_meta( ); builder.model_provider = session_meta.meta.model_provider.clone(); builder.cwd = session_meta.meta.cwd.clone(); + builder.cli_version = Some(session_meta.meta.cli_version.clone()); builder.sandbox_policy = SandboxPolicy::ReadOnly; builder.approval_mode = AskForApproval::OnRequest; if let Some(git) = session_meta.git.as_ref() { @@ -130,16 +133,52 @@ pub(crate) async fn backfill_sessions( otel: Option<&OtelManager>, ) { let timer = otel.and_then(|otel| otel.start_timer(DB_METRIC_BACKFILL_DURATION_MS, &[]).ok()); + let mut backfill_state = match runtime.get_backfill_state().await { + Ok(state) => state, + Err(err) => { + warn!( + "failed to read backfill state at {}: {err}", + config.codex_home.display() + ); + if let Some(otel) = otel { + otel.counter(DB_ERROR_METRIC, 1, &[("stage", "backfill_state_read")]); + } + BackfillState::default() + } + }; + if backfill_state.status == BackfillStatus::Complete { + return; + } + if let Err(err) = runtime.mark_backfill_running().await { + warn!( + "failed to mark backfill running at {}: {err}", + config.codex_home.display() + ); + if let Some(otel) = otel { + otel.counter( + DB_ERROR_METRIC, + 1, + &[("stage", "backfill_state_mark_running")], + ); + } + } else { + backfill_state.status = BackfillStatus::Running; + } + let sessions_root = config.codex_home.join(rollout::SESSIONS_SUBDIR); let archived_root = config.codex_home.join(rollout::ARCHIVED_SESSIONS_SUBDIR); - let mut rollout_paths: Vec<(PathBuf, bool)> = Vec::new(); + let mut rollout_paths: Vec = Vec::new(); for (root, archived) in [(sessions_root, false), (archived_root, true)] { if !tokio::fs::try_exists(&root).await.unwrap_or(false) { continue; } match collect_rollout_paths(&root).await { Ok(paths) => { - rollout_paths.extend(paths.into_iter().map(|path| (path, archived))); + rollout_paths.extend(paths.into_iter().map(|path| BackfillRolloutPath { + watermark: backfill_watermark_for_path(config.codex_home.as_path(), &path), + path, + archived, + })); } Err(err) => { warn!( @@ -149,75 +188,126 @@ pub(crate) async fn backfill_sessions( } } } - rollout_paths.sort_by_key(|(path, _archived)| { - let parsed = path - .file_name() - .and_then(|name| name.to_str()) - .and_then(parse_timestamp_uuid_from_filename) - .unwrap_or((time::OffsetDateTime::UNIX_EPOCH, uuid::Uuid::nil())); - (Reverse(parsed.0), Reverse(parsed.1)) - }); + rollout_paths.sort_by(|a, b| a.watermark.cmp(&b.watermark)); + if let Some(last_watermark) = backfill_state.last_watermark.as_deref() { + rollout_paths.retain(|entry| entry.watermark.as_str() > last_watermark); + } + let mut stats = BackfillStats { scanned: 0, upserted: 0, failed: 0, }; - for (path, archived) in rollout_paths { - stats.scanned = stats.scanned.saturating_add(1); - match extract_metadata_from_rollout(&path, config.model_provider_id.as_str(), otel).await { - Ok(outcome) => { - if outcome.parse_errors > 0 - && let Some(otel) = otel - { - otel.counter( - DB_ERROR_METRIC, - outcome.parse_errors as i64, - &[("stage", "backfill_sessions")], - ); - } - let mut metadata = outcome.metadata; - if archived && metadata.archived_at.is_none() { - let fallback_archived_at = metadata.updated_at; - metadata.archived_at = file_modified_time_utc(&path) - .await - .or(Some(fallback_archived_at)); - } - if let Err(err) = runtime.upsert_thread(&metadata).await { - stats.failed = stats.failed.saturating_add(1); - warn!("failed to upsert rollout {}: {err}", path.display()); - } else { - stats.upserted = stats.upserted.saturating_add(1); - if let Ok(meta_line) = rollout::list::read_session_meta_line(&path).await { - if let Err(err) = runtime - .persist_dynamic_tools( - meta_line.meta.id, - meta_line.meta.dynamic_tools.as_deref(), - ) - .await - { - if let Some(otel) = otel { - otel.counter( - DB_ERROR_METRIC, - 1, - &[("stage", "backfill_dynamic_tools")], - ); - } - warn!("failed to backfill dynamic tools {}: {err}", path.display()); - } - } else { - warn!( - "failed to read session meta for dynamic tools {}", - path.display() + let mut last_watermark = backfill_state.last_watermark.clone(); + for batch in rollout_paths.chunks(BACKFILL_BATCH_SIZE) { + for rollout in batch { + stats.scanned = stats.scanned.saturating_add(1); + match extract_metadata_from_rollout( + &rollout.path, + config.model_provider_id.as_str(), + otel, + ) + .await + { + Ok(outcome) => { + if outcome.parse_errors > 0 + && let Some(otel) = otel + { + otel.counter( + DB_ERROR_METRIC, + outcome.parse_errors as i64, + &[("stage", "backfill_sessions")], ); } + let mut metadata = outcome.metadata; + if rollout.archived && metadata.archived_at.is_none() { + let fallback_archived_at = metadata.updated_at; + metadata.archived_at = file_modified_time_utc(&rollout.path) + .await + .or(Some(fallback_archived_at)); + } + if let Err(err) = runtime.upsert_thread(&metadata).await { + stats.failed = stats.failed.saturating_add(1); + warn!("failed to upsert rollout {}: {err}", rollout.path.display()); + } else { + stats.upserted = stats.upserted.saturating_add(1); + if let Ok(meta_line) = + rollout::list::read_session_meta_line(&rollout.path).await + { + if let Err(err) = runtime + .persist_dynamic_tools( + meta_line.meta.id, + meta_line.meta.dynamic_tools.as_deref(), + ) + .await + { + if let Some(otel) = otel { + otel.counter( + DB_ERROR_METRIC, + 1, + &[("stage", "backfill_dynamic_tools")], + ); + } + warn!( + "failed to backfill dynamic tools {}: {err}", + rollout.path.display() + ); + } + } else { + warn!( + "failed to read session meta for dynamic tools {}", + rollout.path.display() + ); + } + } + } + Err(err) => { + stats.failed = stats.failed.saturating_add(1); + warn!( + "failed to extract rollout {}: {err}", + rollout.path.display() + ); } } - Err(err) => { - stats.failed = stats.failed.saturating_add(1); - warn!("failed to extract rollout {}: {err}", path.display()); + } + + if let Some(last_entry) = batch.last() { + if let Err(err) = runtime + .checkpoint_backfill(last_entry.watermark.as_str()) + .await + { + warn!( + "failed to checkpoint backfill at {}: {err}", + config.codex_home.display() + ); + if let Some(otel) = otel { + otel.counter( + DB_ERROR_METRIC, + 1, + &[("stage", "backfill_state_checkpoint")], + ); + } + } else { + last_watermark = Some(last_entry.watermark.clone()); } } } + if let Err(err) = runtime + .mark_backfill_complete(last_watermark.as_deref()) + .await + { + warn!( + "failed to mark backfill complete at {}: {err}", + config.codex_home.display() + ); + if let Some(otel) = otel { + otel.counter( + DB_ERROR_METRIC, + 1, + &[("stage", "backfill_state_mark_complete")], + ); + } + } info!( "state db backfill scanned={}, upserted={}, failed={}", @@ -247,6 +337,20 @@ pub(crate) async fn backfill_sessions( } } +#[derive(Debug, Clone)] +struct BackfillRolloutPath { + watermark: String, + path: PathBuf, + archived: bool, +} + +fn backfill_watermark_for_path(codex_home: &Path, path: &Path) -> String { + path.strip_prefix(codex_home) + .unwrap_or(path) + .to_string_lossy() + .replace('\\', "/") +} + async fn file_modified_time_utc(path: &Path) -> Option> { let modified = tokio::fs::metadata(path).await.ok()?.modified().ok()?; let updated_at: DateTime = modified.into(); @@ -276,9 +380,28 @@ async fn collect_rollout_paths(root: &Path) -> std::io::Result> { continue; } }; - while let Some(entry) = read_dir.next_entry().await? { + loop { + let next_entry = match read_dir.next_entry().await { + Ok(next_entry) => next_entry, + Err(err) => { + warn!( + "failed to read directory entry under {}: {err}", + dir.display() + ); + continue; + } + }; + let Some(entry) = next_entry else { + break; + }; let path = entry.path(); - let file_type = entry.file_type().await?; + let file_type = match entry.file_type().await { + Ok(file_type) => file_type, + Err(err) => { + warn!("failed to read file type for {}: {err}", path.display()); + continue; + } + }; if file_type.is_dir() { stack.push(path); continue; @@ -312,10 +435,13 @@ mod tests { use codex_protocol::protocol::SessionMeta; use codex_protocol::protocol::SessionMetaLine; use codex_protocol::protocol::SessionSource; + use codex_state::BackfillStatus; use codex_state::ThreadMetadataBuilder; use pretty_assertions::assert_eq; use std::fs::File; use std::io::Write; + use std::path::Path; + use std::path::PathBuf; use tempfile::tempdir; use uuid::Uuid; @@ -393,4 +519,108 @@ mod tests { assert_eq!(builder, expected); } + + #[tokio::test] + async fn backfill_sessions_resumes_from_watermark_and_marks_complete() { + let dir = tempdir().expect("tempdir"); + let codex_home = dir.path().to_path_buf(); + let first_uuid = Uuid::new_v4(); + let second_uuid = Uuid::new_v4(); + let first_path = write_rollout_in_sessions( + codex_home.as_path(), + "2026-01-27T12-34-56", + "2026-01-27T12:34:56Z", + first_uuid, + ); + let second_path = write_rollout_in_sessions( + codex_home.as_path(), + "2026-01-27T12-35-56", + "2026-01-27T12:35:56Z", + second_uuid, + ); + + let runtime = + codex_state::StateRuntime::init(codex_home.clone(), "test-provider".to_string(), None) + .await + .expect("initialize runtime"); + let first_watermark = + backfill_watermark_for_path(codex_home.as_path(), first_path.as_path()); + runtime.mark_backfill_running().await.expect("mark running"); + runtime + .checkpoint_backfill(first_watermark.as_str()) + .await + .expect("checkpoint first watermark"); + + let mut config = crate::config::test_config(); + config.codex_home = codex_home.clone(); + config.model_provider_id = "test-provider".to_string(); + backfill_sessions(runtime.as_ref(), &config, None).await; + + let first_id = ThreadId::from_string(&first_uuid.to_string()).expect("first thread id"); + let second_id = ThreadId::from_string(&second_uuid.to_string()).expect("second thread id"); + assert_eq!( + runtime + .get_thread(first_id) + .await + .expect("get first thread"), + None + ); + assert!( + runtime + .get_thread(second_id) + .await + .expect("get second thread") + .is_some() + ); + + let state = runtime + .get_backfill_state() + .await + .expect("get backfill state"); + assert_eq!(state.status, BackfillStatus::Complete); + assert_eq!( + state.last_watermark, + Some(backfill_watermark_for_path( + codex_home.as_path(), + second_path.as_path() + )) + ); + assert!(state.last_success_at.is_some()); + } + + fn write_rollout_in_sessions( + codex_home: &Path, + filename_ts: &str, + event_ts: &str, + thread_uuid: Uuid, + ) -> PathBuf { + let id = ThreadId::from_string(&thread_uuid.to_string()).expect("thread id"); + let sessions_dir = codex_home.join("sessions"); + std::fs::create_dir_all(sessions_dir.as_path()).expect("create sessions dir"); + let path = sessions_dir.join(format!("rollout-{filename_ts}-{thread_uuid}.jsonl")); + let session_meta = SessionMeta { + id, + forked_from_id: None, + timestamp: event_ts.to_string(), + cwd: codex_home.to_path_buf(), + originator: "cli".to_string(), + cli_version: "0.0.0".to_string(), + source: SessionSource::default(), + model_provider: Some("test-provider".to_string()), + base_instructions: None, + dynamic_tools: None, + }; + let session_meta_line = SessionMetaLine { + meta: session_meta, + git: None, + }; + let rollout_line = RolloutLine { + timestamp: event_ts.to_string(), + item: RolloutItem::SessionMeta(session_meta_line), + }; + let json = serde_json::to_string(&rollout_line).expect("serialize rollout"); + let mut file = File::create(&path).expect("create rollout"); + writeln!(file, "{json}").expect("write rollout"); + path + } } diff --git a/codex-rs/core/src/rollout/mod.rs b/codex-rs/core/src/rollout/mod.rs index 60775d04b0..5e6bd9bbff 100644 --- a/codex-rs/core/src/rollout/mod.rs +++ b/codex-rs/core/src/rollout/mod.rs @@ -24,6 +24,7 @@ pub use list::find_thread_path_by_id_str as find_conversation_path_by_id_str; pub use list::rollout_date_parts; pub use recorder::RolloutRecorder; pub use recorder::RolloutRecorderParams; +pub use session_index::find_thread_name_by_id; pub use session_index::find_thread_path_by_name_str; #[cfg(test)] diff --git a/codex-rs/core/src/rollout/recorder.rs b/codex-rs/core/src/rollout/recorder.rs index 265ff2bde2..c42bbb7974 100644 --- a/codex-rs/core/src/rollout/recorder.rs +++ b/codex-rs/core/src/rollout/recorder.rs @@ -6,6 +6,7 @@ use std::io::Error as IoError; use std::path::Path; use std::path::PathBuf; +use chrono::SecondsFormat; use codex_protocol::ThreadId; use codex_protocol::dynamic_tools::DynamicToolSpec; use codex_protocol::models::BaseInstructions; @@ -18,11 +19,13 @@ use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::{self}; use tokio::sync::oneshot; use tracing::info; +use tracing::trace; use tracing::warn; use super::ARCHIVED_SESSIONS_SUBDIR; use super::SESSIONS_SUBDIR; use super::list::Cursor; +use super::list::ThreadItem; use super::list::ThreadListConfig; use super::list::ThreadListLayout; use super::list::ThreadSortKey; @@ -120,8 +123,7 @@ impl RolloutRecorder { model_providers: Option<&[String]>, default_provider: &str, ) -> std::io::Result { - let stage = "list_threads"; - let page = get_threads( + Self::list_threads_with_db_fallback( codex_home, page_size, cursor, @@ -129,35 +131,9 @@ impl RolloutRecorder { allowed_sources, model_providers, default_provider, - ) - .await?; - - // TODO(jif): drop after sqlite migration phase 1 - let state_db_ctx = state_db::open_if_present(codex_home, default_provider).await; - if let Some(db_ids) = state_db::list_thread_ids_db( - state_db_ctx.as_deref(), - codex_home, - page_size, - cursor, - sort_key, - allowed_sources, - model_providers, false, - stage, ) .await - { - if page.items.len() != db_ids.len() { - state_db::record_discrepancy(stage, "bad_len"); - return Ok(page); - } - for (id, item) in db_ids.iter().zip(page.items.iter()) { - if !item.path.display().to_string().contains(&id.to_string()) { - state_db::record_discrepancy(stage, "bad_id"); - } - } - } - Ok(page) } /// List archived threads (rollout files) under the archived sessions directory. @@ -170,25 +146,32 @@ impl RolloutRecorder { model_providers: Option<&[String]>, default_provider: &str, ) -> std::io::Result { - let stage = "list_archived_threads"; - let root = codex_home.join(ARCHIVED_SESSIONS_SUBDIR); - let page = get_threads_in_root( - root, + Self::list_threads_with_db_fallback( + codex_home, page_size, cursor, sort_key, - ThreadListConfig { - allowed_sources, - model_providers, - default_provider, - layout: ThreadListLayout::Flat, - }, + allowed_sources, + model_providers, + default_provider, + true, ) - .await?; + .await + } - // TODO(jif): drop after sqlite migration phase 1 + #[allow(clippy::too_many_arguments)] + async fn list_threads_with_db_fallback( + codex_home: &Path, + page_size: usize, + cursor: Option<&Cursor>, + sort_key: ThreadSortKey, + allowed_sources: &[SessionSource], + model_providers: Option<&[String]>, + default_provider: &str, + archived: bool, + ) -> std::io::Result { let state_db_ctx = state_db::open_if_present(codex_home, default_provider).await; - if let Some(db_ids) = state_db::list_thread_ids_db( + if let Some(db_page) = state_db::list_threads_db( state_db_ctx.as_deref(), codex_home, page_size, @@ -196,22 +179,42 @@ impl RolloutRecorder { sort_key, allowed_sources, model_providers, - true, - stage, + archived, ) .await { - if page.items.len() != db_ids.len() { - state_db::record_discrepancy(stage, "bad_len"); - return Ok(page); - } - for (id, item) in db_ids.iter().zip(page.items.iter()) { - if !item.path.display().to_string().contains(&id.to_string()) { - state_db::record_discrepancy(stage, "bad_id"); - } - } + return Ok(db_page.into()); } - Ok(page) + tracing::error!("Falling back on rollout system"); + state_db::record_discrepancy("list_threads_with_db_fallback", "falling_back"); + + if archived { + let root = codex_home.join(ARCHIVED_SESSIONS_SUBDIR); + return get_threads_in_root( + root, + page_size, + cursor, + sort_key, + ThreadListConfig { + allowed_sources, + model_providers, + default_provider, + layout: ThreadListLayout::Flat, + }, + ) + .await; + } + + get_threads( + codex_home, + page_size, + cursor, + sort_key, + allowed_sources, + model_providers, + default_provider, + ) + .await } /// Find the newest recorded thread path, optionally filtering to a matching cwd. @@ -226,9 +229,37 @@ impl RolloutRecorder { default_provider: &str, filter_cwd: Option<&Path>, ) -> std::io::Result> { + let state_db_ctx = state_db::open_if_present(codex_home, default_provider).await; + if state_db_ctx.is_some() { + let mut db_cursor = cursor.cloned(); + loop { + let Some(db_page) = state_db::list_threads_db( + state_db_ctx.as_deref(), + codex_home, + page_size, + db_cursor.as_ref(), + sort_key, + allowed_sources, + model_providers, + false, + ) + .await + else { + break; + }; + if let Some(path) = select_resume_path_from_db_page(&db_page, filter_cwd) { + return Ok(Some(path)); + } + db_cursor = db_page.next_anchor.map(Into::into); + if db_cursor.is_none() { + break; + } + } + } + let mut cursor = cursor.cloned(); loop { - let page = Self::list_threads( + let page = get_threads( codex_home, page_size, cursor.as_ref(), @@ -381,7 +412,7 @@ impl RolloutRecorder { pub(crate) async fn load_rollout_items( path: &Path, ) -> std::io::Result<(Vec, Option, usize)> { - info!("Resuming rollout from {path:?}"); + trace!("Resuming rollout from {path:?}"); let text = tokio::fs::read_to_string(path).await?; if text.trim().is_empty() { return Err(IoError::other("empty session file")); @@ -428,7 +459,7 @@ impl RolloutRecorder { } }, Err(e) => { - warn!("failed to parse rollout line: {e}"); + trace!("failed to parse rollout line: {e}"); parse_errors = parse_errors.saturating_add(1); } } @@ -562,6 +593,7 @@ async fn rollout_writer( default_provider.as_str(), state_builder.as_ref(), std::slice::from_ref(&rollout_item), + None, ) .await; } @@ -645,10 +677,46 @@ impl JsonlWriter { } } +impl From for ThreadsPage { + fn from(db_page: codex_state::ThreadsPage) -> Self { + let items = db_page + .items + .into_iter() + .map(|item| ThreadItem { + path: item.rollout_path, + thread_id: Some(item.id), + first_user_message: item.first_user_message, + cwd: Some(item.cwd), + git_branch: item.git_branch, + git_sha: item.git_sha, + git_origin_url: item.git_origin_url, + source: Some( + serde_json::from_value(Value::String(item.source)) + .unwrap_or(SessionSource::Unknown), + ), + model_provider: Some(item.model_provider), + cli_version: Some(item.cli_version), + created_at: Some(item.created_at.to_rfc3339_opts(SecondsFormat::Secs, true)), + updated_at: Some(item.updated_at.to_rfc3339_opts(SecondsFormat::Secs, true)), + }) + .collect(); + Self { + items, + next_cursor: db_page.next_anchor.map(Into::into), + num_scanned_files: db_page.num_scanned_rows, + reached_scan_cap: false, + } + } +} + fn select_resume_path(page: &ThreadsPage, filter_cwd: Option<&Path>) -> Option { match filter_cwd { Some(cwd) => page.items.iter().find_map(|item| { - if session_cwd_matches(&item.head, cwd) { + if item + .cwd + .as_ref() + .is_some_and(|session_cwd| cwd_matches(session_cwd, cwd)) + { Some(item.path.clone()) } else { None @@ -658,22 +726,28 @@ fn select_resume_path(page: &ThreadsPage, filter_cwd: Option<&Path>) -> Option

bool { - let Some(session_cwd) = extract_session_cwd(head) else { - return false; - }; +fn select_resume_path_from_db_page( + page: &codex_state::ThreadsPage, + filter_cwd: Option<&Path>, +) -> Option { + match filter_cwd { + Some(cwd) => page.items.iter().find_map(|item| { + if cwd_matches(item.cwd.as_path(), cwd) { + Some(item.rollout_path.clone()) + } else { + None + } + }), + None => page.items.first().map(|item| item.rollout_path.clone()), + } +} + +fn cwd_matches(session_cwd: &Path, cwd: &Path) -> bool { if let (Ok(ca), Ok(cb)) = ( - path_utils::normalize_for_path_comparison(&session_cwd), + path_utils::normalize_for_path_comparison(session_cwd), path_utils::normalize_for_path_comparison(cwd), ) { return ca == cb; } session_cwd == cwd } - -fn extract_session_cwd(head: &[serde_json::Value]) -> Option { - head.iter().find_map(|value| { - let meta_line = serde_json::from_value::(value.clone()).ok()?; - Some(meta_line.meta.cwd) - }) -} diff --git a/codex-rs/core/src/rollout/tests.rs b/codex-rs/core/src/rollout/tests.rs index ee750b126d..a4bd72433e 100644 --- a/codex-rs/core/src/rollout/tests.rs +++ b/codex-rs/core/src/rollout/tests.rs @@ -7,6 +7,7 @@ use std::fs::{self}; use std::io::Write; use std::path::Path; +use chrono::TimeZone; use pretty_assertions::assert_eq; use tempfile::TempDir; use time::Duration; @@ -22,6 +23,8 @@ use crate::rollout::list::ThreadItem; use crate::rollout::list::ThreadSortKey; use crate::rollout::list::ThreadsPage; use crate::rollout::list::get_threads; +use crate::rollout::list::read_head_for_summary; +use crate::rollout::recorder::RolloutRecorder; use crate::rollout::rollout_date_parts; use anyhow::Result; use codex_protocol::ThreadId; @@ -45,6 +48,254 @@ fn provider_vec(providers: &[&str]) -> Vec { .collect() } +fn thread_id_from_uuid(uuid: Uuid) -> ThreadId { + ThreadId::from_string(&uuid.to_string()).expect("valid thread id") +} + +async fn insert_state_db_thread( + home: &Path, + thread_id: ThreadId, + rollout_path: &Path, + archived: bool, +) { + let runtime = + codex_state::StateRuntime::init(home.to_path_buf(), TEST_PROVIDER.to_string(), None) + .await + .expect("state db should initialize"); + runtime + .mark_backfill_complete(None) + .await + .expect("backfill should be complete"); + let created_at = chrono::Utc + .with_ymd_and_hms(2025, 1, 3, 12, 0, 0) + .single() + .expect("valid datetime"); + let mut builder = codex_state::ThreadMetadataBuilder::new( + thread_id, + rollout_path.to_path_buf(), + created_at, + SessionSource::Cli, + ); + builder.model_provider = Some(TEST_PROVIDER.to_string()); + builder.cwd = home.to_path_buf(); + if archived { + builder.archived_at = Some(created_at); + } + let mut metadata = builder.build(TEST_PROVIDER); + metadata.first_user_message = Some("Hello from user".to_string()); + runtime + .upsert_thread(&metadata) + .await + .expect("state db upsert should succeed"); +} + +#[tokio::test] +async fn list_threads_prefers_state_db_when_available() { + let temp = TempDir::new().unwrap(); + let home = temp.path(); + let fs_uuid = Uuid::from_u128(101); + write_session_file( + home, + "2025-01-03T13-00-00", + fs_uuid, + 1, + Some(SessionSource::Cli), + ) + .unwrap(); + + let db_uuid = Uuid::from_u128(102); + let db_thread_id = ThreadId::from_string(&db_uuid.to_string()).expect("valid thread id"); + let db_rollout_path = home.join(format!( + "sessions/2025/01/03/rollout-2025-01-03T12-00-00-{db_uuid}.jsonl" + )); + insert_state_db_thread(home, db_thread_id, db_rollout_path.as_path(), false).await; + + let page = RolloutRecorder::list_threads( + home, + 10, + None, + ThreadSortKey::CreatedAt, + NO_SOURCE_FILTER, + None, + TEST_PROVIDER, + ) + .await + .expect("thread listing should succeed"); + + assert_eq!(page.items.len(), 1); + assert_eq!(page.items[0].path, db_rollout_path); + assert_eq!(page.items[0].thread_id, Some(db_thread_id)); + assert_eq!(page.items[0].cwd, Some(home.to_path_buf())); + assert_eq!( + page.items[0].first_user_message.as_deref(), + Some("Hello from user") + ); +} + +#[tokio::test] +async fn list_archived_threads_prefers_state_db_when_available() { + let temp = TempDir::new().unwrap(); + let home = temp.path(); + let archived_root = home.join("archived_sessions"); + fs::create_dir_all(&archived_root).unwrap(); + let fs_uuid = Uuid::from_u128(201); + let fs_path = archived_root.join(format!("rollout-2025-01-03T13-00-00-{fs_uuid}.jsonl")); + fs::write(&fs_path, "{\"type\":\"session_meta\",\"payload\":{}}\n").unwrap(); + + let db_uuid = Uuid::from_u128(202); + let db_thread_id = ThreadId::from_string(&db_uuid.to_string()).expect("valid thread id"); + let db_rollout_path = + archived_root.join(format!("rollout-2025-01-03T12-00-00-{db_uuid}.jsonl")); + insert_state_db_thread(home, db_thread_id, db_rollout_path.as_path(), true).await; + + let page = RolloutRecorder::list_archived_threads( + home, + 10, + None, + ThreadSortKey::CreatedAt, + NO_SOURCE_FILTER, + None, + TEST_PROVIDER, + ) + .await + .expect("archived thread listing should succeed"); + + assert_eq!(page.items.len(), 1); + assert_eq!(page.items[0].path, db_rollout_path); +} + +#[tokio::test] +async fn list_threads_db_excludes_archived_entries() { + let temp = TempDir::new().unwrap(); + let home = temp.path(); + let sessions_root = home.join("sessions/2025/01/03"); + let archived_root = home.join("archived_sessions"); + fs::create_dir_all(&sessions_root).unwrap(); + fs::create_dir_all(&archived_root).unwrap(); + + let active_uuid = Uuid::from_u128(211); + let active_thread_id = + ThreadId::from_string(&active_uuid.to_string()).expect("valid active thread id"); + let active_rollout_path = + sessions_root.join(format!("rollout-2025-01-03T12-00-00-{active_uuid}.jsonl")); + insert_state_db_thread(home, active_thread_id, active_rollout_path.as_path(), false).await; + + let archived_uuid = Uuid::from_u128(212); + let archived_thread_id = + ThreadId::from_string(&archived_uuid.to_string()).expect("valid archived thread id"); + let archived_rollout_path = + archived_root.join(format!("rollout-2025-01-03T11-00-00-{archived_uuid}.jsonl")); + insert_state_db_thread( + home, + archived_thread_id, + archived_rollout_path.as_path(), + true, + ) + .await; + + let page = RolloutRecorder::list_threads( + home, + 10, + None, + ThreadSortKey::CreatedAt, + NO_SOURCE_FILTER, + None, + TEST_PROVIDER, + ) + .await + .expect("thread listing should succeed"); + + assert_eq!(page.items.len(), 1); + assert_eq!(page.items[0].path, active_rollout_path); +} + +#[tokio::test] +async fn list_threads_falls_back_to_files_when_state_db_is_unavailable() { + let temp = TempDir::new().unwrap(); + let home = temp.path(); + let fs_uuid = Uuid::from_u128(301); + write_session_file( + home, + "2025-01-03T13-00-00", + fs_uuid, + 1, + Some(SessionSource::Cli), + ) + .unwrap(); + + let page = RolloutRecorder::list_threads( + home, + 10, + None, + ThreadSortKey::CreatedAt, + NO_SOURCE_FILTER, + None, + TEST_PROVIDER, + ) + .await + .expect("thread listing should succeed"); + + assert_eq!(page.items.len(), 1); + let file_name = page.items[0] + .path + .file_name() + .and_then(|value| value.to_str()) + .expect("rollout file name should be utf8"); + assert!( + file_name.contains(&fs_uuid.to_string()), + "expected file path from filesystem listing, got: {file_name}" + ); +} + +#[tokio::test] +async fn find_thread_path_falls_back_when_db_path_is_stale() { + let temp = TempDir::new().unwrap(); + let home = temp.path(); + let uuid = Uuid::from_u128(302); + let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id"); + let ts = "2025-01-03T13-00-00"; + write_session_file(home, ts, uuid, 1, Some(SessionSource::Cli)).unwrap(); + let fs_rollout_path = home.join(format!("sessions/2025/01/03/rollout-{ts}-{uuid}.jsonl")); + + let stale_db_path = home.join(format!( + "sessions/2099/01/01/rollout-2099-01-01T00-00-00-{uuid}.jsonl" + )); + insert_state_db_thread(home, thread_id, stale_db_path.as_path(), false).await; + + let found = crate::rollout::find_thread_path_by_id_str(home, &uuid.to_string()) + .await + .expect("lookup should succeed"); + assert_eq!(found, Some(fs_rollout_path.clone())); + assert_state_db_rollout_path(home, thread_id, Some(fs_rollout_path.as_path())).await; +} + +#[tokio::test] +async fn find_thread_path_repairs_missing_db_row_after_filesystem_fallback() { + let temp = TempDir::new().unwrap(); + let home = temp.path(); + let uuid = Uuid::from_u128(303); + let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id"); + let ts = "2025-01-03T13-00-00"; + write_session_file(home, ts, uuid, 1, Some(SessionSource::Cli)).unwrap(); + let fs_rollout_path = home.join(format!("sessions/2025/01/03/rollout-{ts}-{uuid}.jsonl")); + + // Create an empty state DB so lookup takes the DB-first path and then falls back to files. + let _runtime = + codex_state::StateRuntime::init(home.to_path_buf(), TEST_PROVIDER.to_string(), None) + .await + .expect("state db should initialize"); + _runtime + .mark_backfill_complete(None) + .await + .expect("backfill should be complete"); + + let found = crate::rollout::find_thread_path_by_id_str(home, &uuid.to_string()) + .await + .expect("lookup should succeed"); + assert_eq!(found, Some(fs_rollout_path.clone())); + assert_state_db_rollout_path(home, thread_id, Some(fs_rollout_path.as_path())).await; +} + #[test] fn rollout_date_parts_extracts_directory_components() { let file_name = OsStr::new("rollout-2025-03-01T09-00-00-123.jsonl"); @@ -55,6 +306,22 @@ fn rollout_date_parts_extracts_directory_components() { ); } +async fn assert_state_db_rollout_path( + home: &Path, + thread_id: ThreadId, + expected_path: Option<&Path>, +) { + let runtime = + codex_state::StateRuntime::init(home.to_path_buf(), TEST_PROVIDER.to_string(), None) + .await + .expect("state db should initialize"); + let path = runtime + .find_rollout_path_by_id(thread_id, Some(false)) + .await + .expect("state db lookup should succeed"); + assert_eq!(path.as_deref(), expected_path); +} + fn write_session_file( root: &Path, ts_str: &str, @@ -311,37 +578,6 @@ async fn test_list_conversations_latest_first() { .join("01") .join(format!("rollout-2025-01-01T12-00-00-{u1}.jsonl")); - let head_3 = vec![serde_json::json!({ - "id": u3, - "timestamp": "2025-01-03T12-00-00", - "cwd": ".", - "originator": "test_originator", - "cli_version": "test_version", - "source": "vscode", - "model_provider": "test-provider", - "base_instructions": null, - })]; - let head_2 = vec![serde_json::json!({ - "id": u2, - "timestamp": "2025-01-02T12-00-00", - "cwd": ".", - "originator": "test_originator", - "cli_version": "test_version", - "source": "vscode", - "model_provider": "test-provider", - "base_instructions": null, - })]; - let head_1 = vec![serde_json::json!({ - "id": u1, - "timestamp": "2025-01-01T12-00-00", - "cwd": ".", - "originator": "test_originator", - "cli_version": "test_version", - "source": "vscode", - "model_provider": "test-provider", - "base_instructions": null, - })]; - let updated_times: Vec> = page.items.iter().map(|i| i.updated_at.clone()).collect(); @@ -349,19 +585,43 @@ async fn test_list_conversations_latest_first() { items: vec![ ThreadItem { path: p1, - head: head_3, + thread_id: Some(thread_id_from_uuid(u3)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some("2025-01-03T12-00-00".into()), updated_at: updated_times.first().cloned().flatten(), }, ThreadItem { path: p2, - head: head_2, + thread_id: Some(thread_id_from_uuid(u2)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some("2025-01-02T12-00-00".into()), updated_at: updated_times.get(1).cloned().flatten(), }, ThreadItem { path: p3, - head: head_1, + thread_id: Some(thread_id_from_uuid(u1)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some("2025-01-01T12-00-00".into()), updated_at: updated_times.get(2).cloned().flatten(), }, @@ -452,26 +712,6 @@ async fn test_pagination_cursor() { .join("03") .join("04") .join(format!("rollout-2025-03-04T09-00-00-{u4}.jsonl")); - let head_5 = vec![serde_json::json!({ - "id": u5, - "timestamp": "2025-03-05T09-00-00", - "cwd": ".", - "originator": "test_originator", - "cli_version": "test_version", - "source": "vscode", - "model_provider": "test-provider", - "base_instructions": null, - })]; - let head_4 = vec![serde_json::json!({ - "id": u4, - "timestamp": "2025-03-04T09-00-00", - "cwd": ".", - "originator": "test_originator", - "cli_version": "test_version", - "source": "vscode", - "model_provider": "test-provider", - "base_instructions": null, - })]; let updated_page1: Vec> = page1.items.iter().map(|i| i.updated_at.clone()).collect(); let expected_cursor1: Cursor = @@ -480,13 +720,29 @@ async fn test_pagination_cursor() { items: vec![ ThreadItem { path: p5, - head: head_5, + thread_id: Some(thread_id_from_uuid(u5)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some("2025-03-05T09-00-00".into()), updated_at: updated_page1.first().cloned().flatten(), }, ThreadItem { path: p4, - head: head_4, + thread_id: Some(thread_id_from_uuid(u4)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some("2025-03-04T09-00-00".into()), updated_at: updated_page1.get(1).cloned().flatten(), }, @@ -520,26 +776,6 @@ async fn test_pagination_cursor() { .join("03") .join("02") .join(format!("rollout-2025-03-02T09-00-00-{u2}.jsonl")); - let head_3 = vec![serde_json::json!({ - "id": u3, - "timestamp": "2025-03-03T09-00-00", - "cwd": ".", - "originator": "test_originator", - "cli_version": "test_version", - "source": "vscode", - "model_provider": "test-provider", - "base_instructions": null, - })]; - let head_2 = vec![serde_json::json!({ - "id": u2, - "timestamp": "2025-03-02T09-00-00", - "cwd": ".", - "originator": "test_originator", - "cli_version": "test_version", - "source": "vscode", - "model_provider": "test-provider", - "base_instructions": null, - })]; let updated_page2: Vec> = page2.items.iter().map(|i| i.updated_at.clone()).collect(); let expected_cursor2: Cursor = @@ -548,13 +784,29 @@ async fn test_pagination_cursor() { items: vec![ ThreadItem { path: p3, - head: head_3, + thread_id: Some(thread_id_from_uuid(u3)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some("2025-03-03T09-00-00".into()), updated_at: updated_page2.first().cloned().flatten(), }, ThreadItem { path: p2, - head: head_2, + thread_id: Some(thread_id_from_uuid(u2)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some("2025-03-02T09-00-00".into()), updated_at: updated_page2.get(1).cloned().flatten(), }, @@ -582,22 +834,20 @@ async fn test_pagination_cursor() { .join("03") .join("01") .join(format!("rollout-2025-03-01T09-00-00-{u1}.jsonl")); - let head_1 = vec![serde_json::json!({ - "id": u1, - "timestamp": "2025-03-01T09-00-00", - "cwd": ".", - "originator": "test_originator", - "cli_version": "test_version", - "source": "vscode", - "model_provider": "test-provider", - "base_instructions": null, - })]; let updated_page3: Vec> = page3.items.iter().map(|i| i.updated_at.clone()).collect(); let expected_page3 = ThreadsPage { items: vec![ThreadItem { path: p1, - head: head_1, + thread_id: Some(thread_id_from_uuid(u1)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some("2025-03-01T09-00-00".into()), updated_at: updated_page3.first().cloned().flatten(), }], @@ -665,20 +915,18 @@ async fn test_get_thread_contents() { .join("04") .join("01") .join(format!("rollout-2025-04-01T10-30-00-{uuid}.jsonl")); - let expected_head = vec![serde_json::json!({ - "id": uuid, - "timestamp": ts, - "cwd": ".", - "originator": "test_originator", - "cli_version": "test_version", - "source": "vscode", - "model_provider": "test-provider", - "base_instructions": null, - })]; let expected_page = ThreadsPage { items: vec![ThreadItem { path: expected_path, - head: expected_head, + thread_id: Some(thread_id_from_uuid(uuid)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some(ts.into()), updated_at: page.items[0].updated_at.clone(), }], @@ -745,13 +993,12 @@ async fn test_base_instructions_missing_in_meta_defaults_to_null() { .await .unwrap(); - let head = page - .items - .first() - .and_then(|item| item.head.first()) + let head = read_head_for_summary(&page.items[0].path) + .await .expect("session meta head"); + let first = head.first().expect("first head entry"); assert_eq!( - head.get("base_instructions"), + first.get("base_instructions"), Some(&serde_json::Value::Null) ); } @@ -789,12 +1036,11 @@ async fn test_base_instructions_present_in_meta_is_preserved() { .await .unwrap(); - let head = page - .items - .first() - .and_then(|item| item.head.first()) + let head = read_head_for_summary(&page.items[0].path) + .await .expect("session meta head"); - let base = head + let first = head.first().expect("first head entry"); + let base = first .get("base_instructions") .and_then(|value| value.get("text")) .and_then(serde_json::Value::as_str); @@ -974,18 +1220,6 @@ async fn test_stable_ordering_same_second_pagination() { .join("07") .join("01") .join(format!("rollout-2025-07-01T00-00-00-{u2}.jsonl")); - let head = |u: Uuid| -> Vec { - vec![serde_json::json!({ - "id": u, - "timestamp": ts, - "cwd": ".", - "originator": "test_originator", - "cli_version": "test_version", - "source": "vscode", - "model_provider": "test-provider", - "base_instructions": null, - })] - }; let updated_page1: Vec> = page1.items.iter().map(|i| i.updated_at.clone()).collect(); let expected_cursor1: Cursor = serde_json::from_str(&format!("\"{ts}|{u2}\"")).unwrap(); @@ -993,13 +1227,29 @@ async fn test_stable_ordering_same_second_pagination() { items: vec![ ThreadItem { path: p3, - head: head(u3), + thread_id: Some(thread_id_from_uuid(u3)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some(ts.to_string()), updated_at: updated_page1.first().cloned().flatten(), }, ThreadItem { path: p2, - head: head(u2), + thread_id: Some(thread_id_from_uuid(u2)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some(ts.to_string()), updated_at: updated_page1.get(1).cloned().flatten(), }, @@ -1032,7 +1282,15 @@ async fn test_stable_ordering_same_second_pagination() { let expected_page2 = ThreadsPage { items: vec![ThreadItem { path: p1, - head: head(u1), + thread_id: Some(thread_id_from_uuid(u1)), + first_user_message: Some("Hello from user".to_string()), + cwd: Some(Path::new(".").to_path_buf()), + git_branch: None, + git_sha: None, + git_origin_url: None, + source: Some(SessionSource::VSCode), + model_provider: Some(TEST_PROVIDER.to_string()), + cli_version: Some("test_version".to_string()), created_at: Some(ts.to_string()), updated_at: updated_page2.first().cloned().flatten(), }], @@ -1167,13 +1425,7 @@ async fn test_model_provider_filter_selects_only_matching_sessions() -> Result<( let openai_ids: Vec<_> = openai_sessions .items .iter() - .filter_map(|item| { - item.head - .first() - .and_then(|value| value.get("id")) - .and_then(serde_json::Value::as_str) - .map(str::to_string) - }) + .filter_map(|item| item.thread_id.as_ref().map(ToString::to_string)) .collect(); assert!(openai_ids.contains(&openai_id_str)); assert!(openai_ids.contains(&none_id_str)); @@ -1194,10 +1446,8 @@ async fn test_model_provider_filter_selects_only_matching_sessions() -> Result<( let beta_head = beta_sessions .items .first() - .and_then(|item| item.head.first()) - .and_then(|value| value.get("id")) - .and_then(serde_json::Value::as_str); - assert_eq!(beta_head, Some(beta_id_str.as_str())); + .and_then(|item| item.thread_id.as_ref().map(ToString::to_string)); + assert_eq!(beta_head.as_deref(), Some(beta_id_str.as_str())); let unknown_filter = provider_vec(&["unknown"]); let unknown_sessions = get_threads( diff --git a/codex-rs/core/src/sandboxing/mod.rs b/codex-rs/core/src/sandboxing/mod.rs index fca7adda29..bc914abe5b 100644 --- a/codex-rs/core/src/sandboxing/mod.rs +++ b/codex-rs/core/src/sandboxing/mod.rs @@ -51,6 +51,19 @@ pub struct ExecEnv { pub arg0: Option, } +/// Bundled arguments for sandbox transformation. +/// +/// This keeps call sites self-documenting when several fields are optional. +pub(crate) struct SandboxTransformRequest<'a> { + pub spec: CommandSpec, + pub policy: &'a SandboxPolicy, + pub sandbox: SandboxType, + pub sandbox_policy_cwd: &'a Path, + pub codex_linux_sandbox_exe: Option<&'a PathBuf>, + pub use_linux_sandbox_bwrap: bool, + pub windows_sandbox_level: WindowsSandboxLevel, +} + pub enum SandboxPreference { Auto, Require, @@ -104,13 +117,17 @@ impl SandboxManager { pub(crate) fn transform( &self, - mut spec: CommandSpec, - policy: &SandboxPolicy, - sandbox: SandboxType, - sandbox_policy_cwd: &Path, - codex_linux_sandbox_exe: Option<&PathBuf>, - windows_sandbox_level: WindowsSandboxLevel, + request: SandboxTransformRequest<'_>, ) -> Result { + let SandboxTransformRequest { + mut spec, + policy, + sandbox, + sandbox_policy_cwd, + codex_linux_sandbox_exe, + use_linux_sandbox_bwrap, + windows_sandbox_level, + } = request; let mut env = spec.env; if !policy.has_full_network_access() { env.insert( @@ -141,8 +158,12 @@ impl SandboxManager { SandboxType::LinuxSeccomp => { let exe = codex_linux_sandbox_exe .ok_or(SandboxTransformError::MissingLinuxSandboxExecutable)?; - let mut args = - create_linux_sandbox_command_args(command.clone(), policy, sandbox_policy_cwd); + let mut args = create_linux_sandbox_command_args( + command.clone(), + policy, + sandbox_policy_cwd, + use_linux_sandbox_bwrap, + ); let mut full_command = Vec::with_capacity(1 + args.len()); full_command.push(exe.to_string_lossy().to_string()); full_command.append(&mut args); diff --git a/codex-rs/core/src/session_prefix.rs b/codex-rs/core/src/session_prefix.rs index 198f5dff90..99283082b6 100644 --- a/codex-rs/core/src/session_prefix.rs +++ b/codex-rs/core/src/session_prefix.rs @@ -1,5 +1,3 @@ -use codex_protocol::models::ContentItem; - /// Helpers for identifying model-visible "session prefix" messages. /// /// A session prefix is a user-role message that carries configuration or state needed by @@ -15,12 +13,3 @@ pub(crate) fn is_session_prefix(text: &str) -> bool { let lowered = trimmed.to_ascii_lowercase(); lowered.starts_with(ENVIRONMENT_CONTEXT_OPEN_TAG) || lowered.starts_with(TURN_ABORTED_OPEN_TAG) } - -/// Returns true if `text` starts with a session prefix marker (case-insensitive). -pub(crate) fn is_session_prefix_content(content: &[ContentItem]) -> bool { - if let [ContentItem::InputText { text }] = content { - is_session_prefix(text) - } else { - false - } -} diff --git a/codex-rs/core/src/skills/manager.rs b/codex-rs/core/src/skills/manager.rs index 85e0bf20eb..61dcd98f52 100644 --- a/codex-rs/core/src/skills/manager.rs +++ b/codex-rs/core/src/skills/manager.rs @@ -6,6 +6,7 @@ use std::sync::RwLock; use codex_utils_absolute_path::AbsolutePathBuf; use toml::Value as TomlValue; +use tracing::info; use tracing::warn; use crate::config::Config; @@ -51,14 +52,11 @@ impl SkillsManager { skill_roots_from_layer_stack_with_agents(&config.config_layer_stack, &config.cwd); let mut outcome = load_skills_from_roots(roots); outcome.disabled_paths = disabled_paths_from_stack(&config.config_layer_stack); - match self.cache_by_cwd.write() { - Ok(mut cache) => { - cache.insert(cwd.to_path_buf(), outcome.clone()); - } - Err(err) => { - err.into_inner().insert(cwd.to_path_buf(), outcome.clone()); - } - } + let mut cache = match self.cache_by_cwd.write() { + Ok(cache) => cache, + Err(err) => err.into_inner(), + }; + cache.insert(cwd.to_path_buf(), outcome.clone()); outcome } @@ -109,22 +107,22 @@ impl SkillsManager { let roots = skill_roots_from_layer_stack_with_agents(&config_layer_stack, cwd); let mut outcome = load_skills_from_roots(roots); outcome.disabled_paths = disabled_paths_from_stack(&config_layer_stack); - match self.cache_by_cwd.write() { - Ok(mut cache) => { - cache.insert(cwd.to_path_buf(), outcome.clone()); - } - Err(err) => { - err.into_inner().insert(cwd.to_path_buf(), outcome.clone()); - } - } + let mut cache = match self.cache_by_cwd.write() { + Ok(cache) => cache, + Err(err) => err.into_inner(), + }; + cache.insert(cwd.to_path_buf(), outcome.clone()); outcome } pub fn clear_cache(&self) { - match self.cache_by_cwd.write() { - Ok(mut cache) => cache.clear(), - Err(err) => err.into_inner().clear(), - } + let mut cache = match self.cache_by_cwd.write() { + Ok(cache) => cache, + Err(err) => err.into_inner(), + }; + let cleared = cache.len(); + cache.clear(); + info!("skills cache cleared ({cleared} entries)"); } } diff --git a/codex-rs/core/src/state/service.rs b/codex-rs/core/src/state/service.rs index d7788f71cb..d9f57cb5f5 100644 --- a/codex-rs/core/src/state/service.rs +++ b/codex-rs/core/src/state/service.rs @@ -4,15 +4,16 @@ use crate::AuthManager; use crate::RolloutRecorder; use crate::agent::AgentControl; use crate::analytics_client::AnalyticsEventsClient; +use crate::client::ModelClient; use crate::exec_policy::ExecPolicyManager; +use crate::file_watcher::FileWatcher; +use crate::hooks::Hooks; use crate::mcp_connection_manager::McpConnectionManager; use crate::models_manager::manager::ModelsManager; use crate::skills::SkillsManager; use crate::state_db::StateDbHandle; use crate::tools::sandboxing::ApprovalStore; -use crate::transport_manager::TransportManager; use crate::unified_exec::UnifiedExecProcessManager; -use crate::user_notification::UserNotifier; use codex_otel::OtelManager; use tokio::sync::Mutex; use tokio::sync::RwLock; @@ -23,7 +24,7 @@ pub(crate) struct SessionServices { pub(crate) mcp_startup_cancellation_token: Mutex, pub(crate) unified_exec_manager: UnifiedExecProcessManager, pub(crate) analytics_events_client: AnalyticsEventsClient, - pub(crate) notifier: UserNotifier, + pub(crate) hooks: Hooks, pub(crate) rollout: Mutex>, pub(crate) user_shell: Arc, pub(crate) show_raw_agent_reasoning: bool, @@ -33,7 +34,9 @@ pub(crate) struct SessionServices { pub(crate) otel_manager: OtelManager, pub(crate) tool_approvals: Mutex, pub(crate) skills_manager: Arc, + pub(crate) file_watcher: Arc, pub(crate) agent_control: AgentControl, pub(crate) state_db: Option, - pub(crate) transport_manager: TransportManager, + /// Session-scoped model client shared across turns. + pub(crate) model_client: ModelClient, } diff --git a/codex-rs/core/src/state/session.rs b/codex-rs/core/src/state/session.rs index deee0d0c7c..29bc14d1cc 100644 --- a/codex-rs/core/src/state/session.rs +++ b/codex-rs/core/src/state/session.rs @@ -24,6 +24,8 @@ pub(crate) struct SessionState { /// TODO(owen): This is a temporary solution to avoid updating a thread's updated_at /// timestamp when resuming a session. Remove this once SQLite is in place. pub(crate) initial_context_seeded: bool, + /// Previous rollout model for one-shot model-switch handling on first turn after resume. + pub(crate) pending_resume_previous_model: Option, } impl SessionState { @@ -38,6 +40,7 @@ impl SessionState { dependency_env: HashMap::new(), mcp_dependency_prompted: HashSet::new(), initial_context_seeded: false, + pending_resume_previous_model: None, } } diff --git a/codex-rs/core/src/state_db.rs b/codex-rs/core/src/state_db.rs index ff95ed946f..ed2e8c2640 100644 --- a/codex-rs/core/src/state_db.rs +++ b/codex-rs/core/src/state_db.rs @@ -14,7 +14,7 @@ use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::SessionSource; use codex_state::DB_METRIC_COMPARE_ERROR; pub use codex_state::LogEntry; -use codex_state::STATE_DB_FILENAME; +use codex_state::STATE_DB_VERSION; use codex_state::ThreadMetadataBuilder; use serde_json::Value; use std::path::Path; @@ -32,11 +32,9 @@ pub(crate) async fn init_if_enabled( config: &Config, otel: Option<&OtelManager>, ) -> Option { - let state_path = config.codex_home.join(STATE_DB_FILENAME); if !config.features.enabled(Feature::Sqlite) { return None; } - let existed = tokio::fs::try_exists(&state_path).await.unwrap_or(false); let runtime = match codex_state::StateRuntime::init( config.codex_home.clone(), config.model_provider_id.clone(), @@ -56,44 +54,45 @@ pub(crate) async fn init_if_enabled( return None; } }; - if !existed { - let runtime_for_backfill = Arc::clone(&runtime); - let config_for_backfill = config.clone(); - let otel_for_backfill = otel.cloned(); - tokio::task::spawn(async move { - metadata::backfill_sessions( - runtime_for_backfill.as_ref(), - &config_for_backfill, - otel_for_backfill.as_ref(), - ) - .await; - }); + let backfill_state = match runtime.get_backfill_state().await { + Ok(state) => state, + Err(err) => { + warn!( + "failed to read backfill state at {}: {err}", + config.codex_home.display() + ); + return None; + } + }; + if backfill_state.status != codex_state::BackfillStatus::Complete { + metadata::backfill_sessions(runtime.as_ref(), config, otel).await; } - Some(runtime) + require_backfill_complete(runtime, config.codex_home.as_path()).await } /// Get the DB if the feature is enabled and the DB exists. pub async fn get_state_db(config: &Config, otel: Option<&OtelManager>) -> Option { - let state_path = config.codex_home.join(STATE_DB_FILENAME); + let state_path = codex_state::state_db_path(config.codex_home.as_path()); if !config.features.enabled(Feature::Sqlite) || !tokio::fs::try_exists(&state_path).await.unwrap_or(false) { return None; } - codex_state::StateRuntime::init( + let runtime = codex_state::StateRuntime::init( config.codex_home.clone(), config.model_provider_id.clone(), otel.cloned(), ) .await - .ok() + .ok()?; + require_backfill_complete(runtime, config.codex_home.as_path()).await } /// Open the state runtime when the SQLite file exists, without feature gating. /// /// This is used for parity checks during the SQLite migration phase. pub async fn open_if_present(codex_home: &Path, default_provider: &str) -> Option { - let db_path = codex_home.join(STATE_DB_FILENAME); + let db_path = codex_state::state_db_path(codex_home); if !tokio::fs::try_exists(&db_path).await.unwrap_or(false) { return None; } @@ -104,7 +103,31 @@ pub async fn open_if_present(codex_home: &Path, default_provider: &str) -> Optio ) .await .ok()?; - Some(runtime) + require_backfill_complete(runtime, codex_home).await +} + +async fn require_backfill_complete( + runtime: StateDbHandle, + codex_home: &Path, +) -> Option { + match runtime.get_backfill_state().await { + Ok(state) if state.status == codex_state::BackfillStatus::Complete => Some(runtime), + Ok(state) => { + warn!( + "state db backfill not complete at {} (status: {})", + codex_home.display(), + state.status.as_str() + ); + None + } + Err(err) => { + warn!( + "failed to read backfill state at {}: {err}", + codex_home.display() + ); + None + } + } } fn cursor_to_anchor(cursor: Option<&Cursor>) -> Option { @@ -181,6 +204,59 @@ pub async fn list_thread_ids_db( } } +/// List thread metadata from SQLite without rollout directory traversal. +#[allow(clippy::too_many_arguments)] +pub async fn list_threads_db( + context: Option<&codex_state::StateRuntime>, + codex_home: &Path, + page_size: usize, + cursor: Option<&Cursor>, + sort_key: ThreadSortKey, + allowed_sources: &[SessionSource], + model_providers: Option<&[String]>, + archived: bool, +) -> Option { + let ctx = context?; + if ctx.codex_home() != codex_home { + warn!( + "state db codex_home mismatch: expected {}, got {}", + ctx.codex_home().display(), + codex_home.display() + ); + } + + let anchor = cursor_to_anchor(cursor); + let allowed_sources: Vec = allowed_sources + .iter() + .map(|value| match serde_json::to_value(value) { + Ok(Value::String(s)) => s, + Ok(other) => other.to_string(), + Err(_) => String::new(), + }) + .collect(); + let model_providers = model_providers.map(<[String]>::to_vec); + match ctx + .list_threads( + page_size, + anchor.as_ref(), + match sort_key { + ThreadSortKey::CreatedAt => codex_state::SortKey::CreatedAt, + ThreadSortKey::UpdatedAt => codex_state::SortKey::UpdatedAt, + }, + allowed_sources.as_slice(), + model_providers.as_deref(), + archived, + ) + .await + { + Ok(page) => Some(page), + Err(err) => { + warn!("state db list_threads failed: {err}"); + None + } + } +} + /// Look up the rollout path for a thread id using SQLite. pub async fn find_rollout_path_by_id( context: Option<&codex_state::StateRuntime>, @@ -228,6 +304,60 @@ pub async fn persist_dynamic_tools( } } +/// Get memory summaries for a thread id using SQLite. +pub async fn get_thread_memory( + context: Option<&codex_state::StateRuntime>, + thread_id: ThreadId, + stage: &str, +) -> Option { + let ctx = context?; + match ctx.get_thread_memory(thread_id).await { + Ok(memory) => memory, + Err(err) => { + warn!("state db get_thread_memory failed during {stage}: {err}"); + None + } + } +} + +/// Upsert memory summaries for a thread id using SQLite. +pub async fn upsert_thread_memory( + context: Option<&codex_state::StateRuntime>, + thread_id: ThreadId, + trace_summary: &str, + memory_summary: &str, + stage: &str, +) -> Option { + let ctx = context?; + match ctx + .upsert_thread_memory(thread_id, trace_summary, memory_summary) + .await + { + Ok(memory) => Some(memory), + Err(err) => { + warn!("state db upsert_thread_memory failed during {stage}: {err}"); + None + } + } +} + +/// Get the last N memories corresponding to a cwd using an exact path match. +pub async fn get_last_n_thread_memories_for_cwd( + context: Option<&codex_state::StateRuntime>, + cwd: &Path, + n: usize, + stage: &str, +) -> Option> { + let ctx = context?; + match ctx.get_last_n_thread_memories_for_cwd(cwd, n).await { + Ok(memories) => Some(memories), + Err(err) => { + warn!("state db get_last_n_thread_memories_for_cwd failed during {stage}: {err}"); + None + } + } +} + /// Reconcile rollout items into SQLite, falling back to scanning the rollout file. pub async fn reconcile_rollout( context: Option<&codex_state::StateRuntime>, @@ -235,6 +365,7 @@ pub async fn reconcile_rollout( default_provider: &str, builder: Option<&ThreadMetadataBuilder>, items: &[RolloutItem], + archived_only: Option, ) { let Some(ctx) = context else { return; @@ -262,7 +393,17 @@ pub async fn reconcile_rollout( return; } }; - if let Err(err) = ctx.upsert_thread(&outcome.metadata).await { + let mut metadata = outcome.metadata; + match archived_only { + Some(true) if metadata.archived_at.is_none() => { + metadata.archived_at = Some(metadata.updated_at); + } + Some(false) => { + metadata.archived_at = None; + } + Some(true) | None => {} + } + if let Err(err) = ctx.upsert_thread(&metadata).await { warn!( "state db reconcile_rollout upsert failed {}: {err}", rollout_path.display() @@ -285,6 +426,56 @@ pub async fn reconcile_rollout( } } +/// Repair a thread's rollout path after filesystem fallback succeeds. +pub async fn read_repair_rollout_path( + context: Option<&codex_state::StateRuntime>, + thread_id: Option, + archived_only: Option, + rollout_path: &Path, +) { + let Some(ctx) = context else { + return; + }; + + if let Some(thread_id) = thread_id + && let Ok(Some(mut metadata)) = ctx.get_thread(thread_id).await + { + metadata.rollout_path = rollout_path.to_path_buf(); + match archived_only { + Some(true) if metadata.archived_at.is_none() => { + metadata.archived_at = Some(metadata.updated_at); + } + Some(false) => { + metadata.archived_at = None; + } + Some(true) | None => {} + } + if let Err(err) = ctx.upsert_thread(&metadata).await { + warn!( + "state db read-repair upsert failed for {}: {err}", + rollout_path.display() + ); + } else { + return; + } + } + + let default_provider = crate::rollout::list::read_session_meta_line(rollout_path) + .await + .ok() + .and_then(|meta| meta.meta.model_provider) + .unwrap_or_default(); + reconcile_rollout( + Some(ctx), + rollout_path, + default_provider.as_str(), + None, + &[], + archived_only, + ) + .await; +} + /// Apply rollout items incrementally to SQLite. pub async fn apply_rollout_items( context: Option<&codex_state::StateRuntime>, @@ -329,7 +520,11 @@ pub fn record_discrepancy(stage: &str, reason: &str) { let _ = metric.counter( DB_METRIC_COMPARE_ERROR, 1, - &[("stage", stage), ("reason", reason)], + &[ + ("stage", stage), + ("reason", reason), + ("version", &STATE_DB_VERSION.to_string()), + ], ); } } diff --git a/codex-rs/core/src/stream_events_utils.rs b/codex-rs/core/src/stream_events_utils.rs index 02d9822510..394f4b9329 100644 --- a/codex-rs/core/src/stream_events_utils.rs +++ b/codex-rs/core/src/stream_events_utils.rs @@ -14,6 +14,7 @@ use crate::parse_turn_item; use crate::proposed_plan_parser::strip_proposed_plan_blocks; use crate::tools::parallel::ToolCallRuntime; use crate::tools::router::ToolRouter; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ResponseItem; @@ -100,15 +101,14 @@ pub(crate) async fn handle_output_item_done( Err(FunctionCallError::MissingLocalShellCallId) => { let msg = "LocalShellCall without call_id or id"; ctx.turn_context - .client - .get_otel_manager() + .otel_manager .log_tool_failed("local_shell", msg); tracing::error!(msg); let response = ResponseInputItem::FunctionCallOutput { call_id: String::new(), output: FunctionCallOutputPayload { - content: msg.to_string(), + body: FunctionCallOutputBody::Text(msg.to_string()), ..Default::default() }, }; @@ -131,7 +131,7 @@ pub(crate) async fn handle_output_item_done( let response = ResponseInputItem::FunctionCallOutput { call_id: String::new(), output: FunctionCallOutputPayload { - content: message, + body: FunctionCallOutputBody::Text(message), ..Default::default() }, }; @@ -236,9 +236,8 @@ pub(crate) fn response_input_to_response_item(input: &ResponseInputItem) -> Opti let output = match result { Ok(call_tool_result) => FunctionCallOutputPayload::from(call_tool_result), Err(err) => FunctionCallOutputPayload { - content: err.clone(), + body: FunctionCallOutputBody::Text(err.clone()), success: Some(false), - ..Default::default() }, }; Some(ResponseItem::FunctionCallOutput { diff --git a/codex-rs/core/src/tasks/compact.rs b/codex-rs/core/src/tasks/compact.rs index 00d882f607..e1fdbfd91f 100644 --- a/codex-rs/core/src/tasks/compact.rs +++ b/codex-rs/core/src/tasks/compact.rs @@ -25,10 +25,7 @@ impl SessionTask for CompactTask { _cancellation_token: CancellationToken, ) -> Option { let session = session.clone_session(); - if crate::compact::should_use_remote_compact_task( - session.as_ref(), - &ctx.client.get_provider(), - ) { + if crate::compact::should_use_remote_compact_task(session.as_ref(), &ctx.provider) { let _ = session.services.otel_manager.counter( "codex.task.compact", 1, diff --git a/codex-rs/core/src/tasks/mod.rs b/codex-rs/core/src/tasks/mod.rs index d5d6f60582..0821ba7bc2 100644 --- a/codex-rs/core/src/tasks/mod.rs +++ b/codex-rs/core/src/tasks/mod.rs @@ -31,6 +31,7 @@ use crate::state::ActiveTurn; use crate::state::RunningTask; use crate::state::TaskKind; use codex_protocol::models::ContentItem; +use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ResponseItem; use codex_protocol::protocol::RolloutItem; use codex_protocol::user_input::UserInput; @@ -40,7 +41,9 @@ pub(crate) use ghost_snapshot::GhostSnapshotTask; pub(crate) use regular::RegularTask; pub(crate) use review::ReviewTask; pub(crate) use undo::UndoTask; +pub(crate) use user_shell::UserShellCommandMode; pub(crate) use user_shell::UserShellCommandTask; +pub(crate) use user_shell::execute_user_shell_command; const GRACEFULL_INTERRUPTION_TIMEOUT_MS: u64 = 100; const TURN_ABORTED_INTERRUPTED_GUIDANCE: &str = "The user interrupted the previous turn on purpose. If any tools/commands were aborted, they may have partially executed; verify current state before retrying."; @@ -158,8 +161,7 @@ impl Session { }; let timer = turn_context - .client - .get_otel_manager() + .otel_manager .start_timer("codex.turn.e2e_duration_ms", &[]) .ok(); @@ -188,15 +190,27 @@ impl Session { last_agent_message: Option, ) { let mut active = self.active_turn.lock().await; - let should_close_processes = if let Some(at) = active.as_mut() + let mut pending_input = Vec::::new(); + let mut should_close_processes = false; + if let Some(at) = active.as_mut() && at.remove_task(&turn_context.sub_id) { + let mut ts = at.turn_state.lock().await; + pending_input = ts.take_pending_input(); + should_close_processes = true; + } + if should_close_processes { *active = None; - true - } else { - false - }; + } drop(active); + if !pending_input.is_empty() { + let pending_response_items = pending_input + .into_iter() + .map(ResponseItem::from) + .collect::>(); + self.record_conversation_items(turn_context.as_ref(), &pending_response_items) + .await; + } if should_close_processes { self.close_unified_exec_processes().await; } diff --git a/codex-rs/core/src/tasks/review.rs b/codex-rs/core/src/tasks/review.rs index cfc554f856..87d6c41933 100644 --- a/codex-rs/core/src/tasks/review.rs +++ b/codex-rs/core/src/tasks/review.rs @@ -82,7 +82,7 @@ async fn start_review_conversation( input: Vec, cancellation_token: CancellationToken, ) -> Option> { - let config = ctx.client.config(); + let config = ctx.config.clone(); let mut sub_agent_config = config.as_ref().clone(); // Carry over review-only feature restrictions so the delegate cannot // re-enable blocked tools (web search, view image). @@ -94,7 +94,7 @@ async fn start_review_conversation( let model = config .review_model .clone() - .unwrap_or_else(|| ctx.client.get_model()); + .unwrap_or_else(|| ctx.model_info.slug.clone()); sub_agent_config.model = Some(model); (run_codex_thread_one_shot( sub_agent_config, diff --git a/codex-rs/core/src/tasks/user_shell.rs b/codex-rs/core/src/tasks/user_shell.rs index a3d38afd2c..d626057d92 100644 --- a/codex-rs/core/src/tasks/user_shell.rs +++ b/codex-rs/core/src/tasks/user_shell.rs @@ -32,9 +32,22 @@ use crate::user_shell_command::user_shell_command_record_item; use super::SessionTask; use super::SessionTaskContext; +use crate::codex::Session; +use codex_protocol::models::ResponseInputItem; +use codex_protocol::models::ResponseItem; const USER_SHELL_TIMEOUT_MS: u64 = 60 * 60 * 1000; // 1 hour +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum UserShellCommandMode { + /// Executes as an independent turn lifecycle (emits TurnStarted/TurnComplete + /// via task lifecycle plumbing). + StandaloneTurn, + /// Executes while another turn is already active. This mode must not emit a + /// second TurnStarted/TurnComplete pair for the same active turn. + ActiveTurnAuxiliary, +} + #[derive(Clone)] pub(crate) struct UserShellCommandTask { command: String, @@ -59,198 +72,246 @@ impl SessionTask for UserShellCommandTask { _input: Vec, cancellation_token: CancellationToken, ) -> Option { - let _ = session - .session - .services - .otel_manager - .counter("codex.task.user_shell", 1, &[]); - - let event = EventMsg::TurnStarted(TurnStartedEvent { - model_context_window: turn_context.client.get_model_context_window(), - collaboration_mode_kind: turn_context.collaboration_mode.mode, - }); - let session = session.clone_session(); - session.send_event(turn_context.as_ref(), event).await; - - // Execute the user's script under their default shell when known; this - // allows commands that use shell features (pipes, &&, redirects, etc.). - // We do not source rc files or otherwise reformat the script. - let use_login_shell = true; - let session_shell = session.user_shell(); - let display_command = session_shell.derive_exec_args(&self.command, use_login_shell); - let exec_command = - maybe_wrap_shell_lc_with_snapshot(&display_command, session_shell.as_ref()); - - let call_id = Uuid::new_v4().to_string(); - let raw_command = self.command.clone(); - let cwd = turn_context.cwd.clone(); - - let parsed_cmd = parse_command(&display_command); - session - .send_event( - turn_context.as_ref(), - EventMsg::ExecCommandBegin(ExecCommandBeginEvent { - call_id: call_id.clone(), - process_id: None, - turn_id: turn_context.sub_id.clone(), - command: display_command.clone(), - cwd: cwd.clone(), - parsed_cmd: parsed_cmd.clone(), - source: ExecCommandSource::UserShell, - interaction_input: None, - }), - ) - .await; - - let exec_env = ExecEnv { - command: exec_command.clone(), - cwd: cwd.clone(), - env: create_env( - &turn_context.shell_environment_policy, - Some(session.conversation_id), - ), - // TODO(zhao-oai): Now that we have ExecExpiration::Cancellation, we - // should use that instead of an "arbitrarily large" timeout here. - expiration: USER_SHELL_TIMEOUT_MS.into(), - sandbox: SandboxType::None, - windows_sandbox_level: turn_context.windows_sandbox_level, - sandbox_permissions: SandboxPermissions::UseDefault, - justification: None, - arg0: None, - }; - - let stdout_stream = Some(StdoutStream { - sub_id: turn_context.sub_id.clone(), - call_id: call_id.clone(), - tx_event: session.get_tx_event(), - }); - - let sandbox_policy = SandboxPolicy::DangerFullAccess; - let exec_result = execute_exec_env(exec_env, &sandbox_policy, stdout_stream) - .or_cancel(&cancellation_token) - .await; - - match exec_result { - Err(CancelErr::Cancelled) => { - let aborted_message = "command aborted by user".to_string(); - let exec_output = ExecToolCallOutput { - exit_code: -1, - stdout: StreamOutput::new(String::new()), - stderr: StreamOutput::new(aborted_message.clone()), - aggregated_output: StreamOutput::new(aborted_message.clone()), - duration: Duration::ZERO, - timed_out: false, - }; - let output_items = [user_shell_command_record_item( - &raw_command, - &exec_output, - &turn_context, - )]; - session - .record_conversation_items(turn_context.as_ref(), &output_items) - .await; - session - .send_event( - turn_context.as_ref(), - EventMsg::ExecCommandEnd(ExecCommandEndEvent { - call_id, - process_id: None, - turn_id: turn_context.sub_id.clone(), - command: display_command.clone(), - cwd: cwd.clone(), - parsed_cmd: parsed_cmd.clone(), - source: ExecCommandSource::UserShell, - interaction_input: None, - stdout: String::new(), - stderr: aborted_message.clone(), - aggregated_output: aborted_message.clone(), - exit_code: -1, - duration: Duration::ZERO, - formatted_output: aborted_message, - }), - ) - .await; - } - Ok(Ok(output)) => { - session - .send_event( - turn_context.as_ref(), - EventMsg::ExecCommandEnd(ExecCommandEndEvent { - call_id: call_id.clone(), - process_id: None, - turn_id: turn_context.sub_id.clone(), - command: display_command.clone(), - cwd: cwd.clone(), - parsed_cmd: parsed_cmd.clone(), - source: ExecCommandSource::UserShell, - interaction_input: None, - stdout: output.stdout.text.clone(), - stderr: output.stderr.text.clone(), - aggregated_output: output.aggregated_output.text.clone(), - exit_code: output.exit_code, - duration: output.duration, - formatted_output: format_exec_output_str( - &output, - turn_context.truncation_policy, - ), - }), - ) - .await; - - let output_items = [user_shell_command_record_item( - &raw_command, - &output, - &turn_context, - )]; - session - .record_conversation_items(turn_context.as_ref(), &output_items) - .await; - } - Ok(Err(err)) => { - error!("user shell command failed: {err:?}"); - let message = format!("execution error: {err:?}"); - let exec_output = ExecToolCallOutput { - exit_code: -1, - stdout: StreamOutput::new(String::new()), - stderr: StreamOutput::new(message.clone()), - aggregated_output: StreamOutput::new(message.clone()), - duration: Duration::ZERO, - timed_out: false, - }; - session - .send_event( - turn_context.as_ref(), - EventMsg::ExecCommandEnd(ExecCommandEndEvent { - call_id, - process_id: None, - turn_id: turn_context.sub_id.clone(), - command: display_command, - cwd, - parsed_cmd, - source: ExecCommandSource::UserShell, - interaction_input: None, - stdout: exec_output.stdout.text.clone(), - stderr: exec_output.stderr.text.clone(), - aggregated_output: exec_output.aggregated_output.text.clone(), - exit_code: exec_output.exit_code, - duration: exec_output.duration, - formatted_output: format_exec_output_str( - &exec_output, - turn_context.truncation_policy, - ), - }), - ) - .await; - let output_items = [user_shell_command_record_item( - &raw_command, - &exec_output, - &turn_context, - )]; - session - .record_conversation_items(turn_context.as_ref(), &output_items) - .await; - } - } + execute_user_shell_command( + session.clone_session(), + turn_context, + self.command.clone(), + cancellation_token, + UserShellCommandMode::StandaloneTurn, + ) + .await; None } } + +pub(crate) async fn execute_user_shell_command( + session: Arc, + turn_context: Arc, + command: String, + cancellation_token: CancellationToken, + mode: UserShellCommandMode, +) { + session + .services + .otel_manager + .counter("codex.task.user_shell", 1, &[]); + + if mode == UserShellCommandMode::StandaloneTurn { + // Auxiliary mode runs within an existing active turn. That turn already + // emitted TurnStarted, so emitting another TurnStarted here would create + // duplicate turn lifecycle events and confuse clients. + let event = EventMsg::TurnStarted(TurnStartedEvent { + model_context_window: turn_context.model_context_window(), + collaboration_mode_kind: turn_context.collaboration_mode.mode, + }); + session.send_event(turn_context.as_ref(), event).await; + } + + // Execute the user's script under their default shell when known; this + // allows commands that use shell features (pipes, &&, redirects, etc.). + // We do not source rc files or otherwise reformat the script. + let use_login_shell = true; + let session_shell = session.user_shell(); + let display_command = session_shell.derive_exec_args(&command, use_login_shell); + let exec_command = maybe_wrap_shell_lc_with_snapshot(&display_command, session_shell.as_ref()); + + let call_id = Uuid::new_v4().to_string(); + let raw_command = command; + let cwd = turn_context.cwd.clone(); + + let parsed_cmd = parse_command(&display_command); + session + .send_event( + turn_context.as_ref(), + EventMsg::ExecCommandBegin(ExecCommandBeginEvent { + call_id: call_id.clone(), + process_id: None, + turn_id: turn_context.sub_id.clone(), + command: display_command.clone(), + cwd: cwd.clone(), + parsed_cmd: parsed_cmd.clone(), + source: ExecCommandSource::UserShell, + interaction_input: None, + }), + ) + .await; + + let exec_env = ExecEnv { + command: exec_command.clone(), + cwd: cwd.clone(), + env: create_env( + &turn_context.shell_environment_policy, + Some(session.conversation_id), + ), + // TODO(zhao-oai): Now that we have ExecExpiration::Cancellation, we + // should use that instead of an "arbitrarily large" timeout here. + expiration: USER_SHELL_TIMEOUT_MS.into(), + sandbox: SandboxType::None, + windows_sandbox_level: turn_context.windows_sandbox_level, + sandbox_permissions: SandboxPermissions::UseDefault, + justification: None, + arg0: None, + }; + + let stdout_stream = Some(StdoutStream { + sub_id: turn_context.sub_id.clone(), + call_id: call_id.clone(), + tx_event: session.get_tx_event(), + }); + + let sandbox_policy = SandboxPolicy::DangerFullAccess; + let exec_result = execute_exec_env(exec_env, &sandbox_policy, stdout_stream) + .or_cancel(&cancellation_token) + .await; + + match exec_result { + Err(CancelErr::Cancelled) => { + let aborted_message = "command aborted by user".to_string(); + let exec_output = ExecToolCallOutput { + exit_code: -1, + stdout: StreamOutput::new(String::new()), + stderr: StreamOutput::new(aborted_message.clone()), + aggregated_output: StreamOutput::new(aborted_message.clone()), + duration: Duration::ZERO, + timed_out: false, + }; + persist_user_shell_output( + &session, + turn_context.as_ref(), + &raw_command, + &exec_output, + mode, + ) + .await; + session + .send_event( + turn_context.as_ref(), + EventMsg::ExecCommandEnd(ExecCommandEndEvent { + call_id, + process_id: None, + turn_id: turn_context.sub_id.clone(), + command: display_command.clone(), + cwd: cwd.clone(), + parsed_cmd: parsed_cmd.clone(), + source: ExecCommandSource::UserShell, + interaction_input: None, + stdout: String::new(), + stderr: aborted_message.clone(), + aggregated_output: aborted_message.clone(), + exit_code: -1, + duration: Duration::ZERO, + formatted_output: aborted_message, + }), + ) + .await; + } + Ok(Ok(output)) => { + session + .send_event( + turn_context.as_ref(), + EventMsg::ExecCommandEnd(ExecCommandEndEvent { + call_id: call_id.clone(), + process_id: None, + turn_id: turn_context.sub_id.clone(), + command: display_command.clone(), + cwd: cwd.clone(), + parsed_cmd: parsed_cmd.clone(), + source: ExecCommandSource::UserShell, + interaction_input: None, + stdout: output.stdout.text.clone(), + stderr: output.stderr.text.clone(), + aggregated_output: output.aggregated_output.text.clone(), + exit_code: output.exit_code, + duration: output.duration, + formatted_output: format_exec_output_str( + &output, + turn_context.truncation_policy, + ), + }), + ) + .await; + + persist_user_shell_output(&session, turn_context.as_ref(), &raw_command, &output, mode) + .await; + } + Ok(Err(err)) => { + error!("user shell command failed: {err:?}"); + let message = format!("execution error: {err:?}"); + let exec_output = ExecToolCallOutput { + exit_code: -1, + stdout: StreamOutput::new(String::new()), + stderr: StreamOutput::new(message.clone()), + aggregated_output: StreamOutput::new(message.clone()), + duration: Duration::ZERO, + timed_out: false, + }; + session + .send_event( + turn_context.as_ref(), + EventMsg::ExecCommandEnd(ExecCommandEndEvent { + call_id, + process_id: None, + turn_id: turn_context.sub_id.clone(), + command: display_command, + cwd, + parsed_cmd, + source: ExecCommandSource::UserShell, + interaction_input: None, + stdout: exec_output.stdout.text.clone(), + stderr: exec_output.stderr.text.clone(), + aggregated_output: exec_output.aggregated_output.text.clone(), + exit_code: exec_output.exit_code, + duration: exec_output.duration, + formatted_output: format_exec_output_str( + &exec_output, + turn_context.truncation_policy, + ), + }), + ) + .await; + persist_user_shell_output( + &session, + turn_context.as_ref(), + &raw_command, + &exec_output, + mode, + ) + .await; + } + } +} + +async fn persist_user_shell_output( + session: &Session, + turn_context: &TurnContext, + raw_command: &str, + exec_output: &ExecToolCallOutput, + mode: UserShellCommandMode, +) { + let output_item = user_shell_command_record_item(raw_command, exec_output, turn_context); + + if mode == UserShellCommandMode::StandaloneTurn { + session + .record_conversation_items(turn_context, std::slice::from_ref(&output_item)) + .await; + return; + } + + let response_input_item = match output_item { + ResponseItem::Message { role, content, .. } => ResponseInputItem::Message { role, content }, + _ => unreachable!("user shell command output record should always be a message"), + }; + + if let Err(items) = session + .inject_response_items(vec![response_input_item]) + .await + { + let response_items = items + .into_iter() + .map(ResponseItem::from) + .collect::>(); + session + .record_conversation_items(turn_context, &response_items) + .await; + } +} diff --git a/codex-rs/core/src/thread_manager.rs b/codex-rs/core/src/thread_manager.rs index 37bd0efabc..e702d9a004 100644 --- a/codex-rs/core/src/thread_manager.rs +++ b/codex-rs/core/src/thread_manager.rs @@ -11,6 +11,8 @@ use crate::codex_thread::CodexThread; use crate::config::Config; use crate::error::CodexErr; use crate::error::Result as CodexResult; +use crate::file_watcher::FileWatcher; +use crate::file_watcher::FileWatcherEvent; use crate::models_manager::manager::ModelsManager; use crate::protocol::Event; use crate::protocol::EventMsg; @@ -31,12 +33,56 @@ use std::path::PathBuf; use std::sync::Arc; #[cfg(any(test, feature = "test-support"))] use tempfile::TempDir; +use tokio::runtime::Handle; +#[cfg(any(test, feature = "test-support"))] +use tokio::runtime::RuntimeFlavor; use tokio::sync::RwLock; use tokio::sync::broadcast; use tracing::warn; const THREAD_CREATED_CHANNEL_CAPACITY: usize = 1024; +fn build_file_watcher(codex_home: PathBuf, skills_manager: Arc) -> Arc { + #[cfg(any(test, feature = "test-support"))] + if let Ok(handle) = Handle::try_current() + && handle.runtime_flavor() == RuntimeFlavor::CurrentThread + { + // The real watcher spins background tasks that can starve the + // current-thread test runtime and cause event waits to time out. + // Integration tests compile with the `test-support` feature. + warn!("using noop file watcher under current-thread test runtime"); + return Arc::new(FileWatcher::noop()); + } + + let file_watcher = match FileWatcher::new(codex_home) { + Ok(file_watcher) => Arc::new(file_watcher), + Err(err) => { + warn!("failed to initialize file watcher: {err}"); + Arc::new(FileWatcher::noop()) + } + }; + + let mut rx = file_watcher.subscribe(); + let skills_manager = Arc::clone(&skills_manager); + if let Ok(handle) = Handle::try_current() { + handle.spawn(async move { + loop { + match rx.recv().await { + Ok(FileWatcherEvent::SkillsChanged { .. }) => { + skills_manager.clear_cache(); + } + Err(broadcast::error::RecvError::Closed) => break, + Err(broadcast::error::RecvError::Lagged(_)) => continue, + } + } + }); + } else { + warn!("file watcher listener skipped: no Tokio runtime available"); + } + + file_watcher +} + /// Represents a newly created Codex thread (formerly called a conversation), including the first event /// (which is [`EventMsg::SessionConfigured`]). pub struct NewThread { @@ -62,6 +108,7 @@ pub(crate) struct ThreadManagerState { auth_manager: Arc, models_manager: Arc, skills_manager: Arc, + file_watcher: Arc, session_source: SessionSource, #[cfg(any(test, feature = "test-support"))] #[allow(dead_code)] @@ -76,15 +123,15 @@ impl ThreadManager { session_source: SessionSource, ) -> Self { let (thread_created_tx, _) = broadcast::channel(THREAD_CREATED_CHANNEL_CAPACITY); + let skills_manager = Arc::new(SkillsManager::new(codex_home.clone())); + let file_watcher = build_file_watcher(codex_home.clone(), Arc::clone(&skills_manager)); Self { state: Arc::new(ThreadManagerState { threads: Arc::new(RwLock::new(HashMap::new())), thread_created_tx, - models_manager: Arc::new(ModelsManager::new( - codex_home.clone(), - auth_manager.clone(), - )), - skills_manager: Arc::new(SkillsManager::new(codex_home)), + models_manager: Arc::new(ModelsManager::new(codex_home, auth_manager.clone())), + skills_manager, + file_watcher, auth_manager, session_source, #[cfg(any(test, feature = "test-support"))] @@ -116,16 +163,19 @@ impl ThreadManager { ) -> Self { let auth_manager = AuthManager::from_auth_for_testing(auth); let (thread_created_tx, _) = broadcast::channel(THREAD_CREATED_CHANNEL_CAPACITY); + let skills_manager = Arc::new(SkillsManager::new(codex_home.clone())); + let file_watcher = build_file_watcher(codex_home.clone(), Arc::clone(&skills_manager)); Self { state: Arc::new(ThreadManagerState { threads: Arc::new(RwLock::new(HashMap::new())), thread_created_tx, models_manager: Arc::new(ModelsManager::with_provider( - codex_home.clone(), + codex_home, auth_manager.clone(), provider, )), - skills_manager: Arc::new(SkillsManager::new(codex_home)), + skills_manager, + file_watcher, auth_manager, session_source: SessionSource::Exec, #[cfg(any(test, feature = "test-support"))] @@ -143,6 +193,10 @@ impl ThreadManager { self.state.skills_manager.clone() } + pub fn subscribe_file_watcher(&self) -> broadcast::Receiver { + self.state.file_watcher.subscribe() + } + pub fn get_models_manager(&self) -> Arc { self.state.models_manager.clone() } @@ -380,6 +434,7 @@ impl ThreadManagerState { session_source: SessionSource, dynamic_tools: Vec, ) -> CodexResult { + self.file_watcher.register_config(&config); let CodexSpawnOk { codex, thread_id, .. } = Codex::spawn( @@ -387,6 +442,7 @@ impl ThreadManagerState { auth_manager, Arc::clone(&self.models_manager), Arc::clone(&self.skills_manager), + Arc::clone(&self.file_watcher), initial_history, session_source, agent_control, diff --git a/codex-rs/core/src/tools/context.rs b/codex-rs/core/src/tools/context.rs index f0bbb158f5..e9edd7db46 100644 --- a/codex-rs/core/src/tools/context.rs +++ b/codex-rs/core/src/tools/context.rs @@ -5,7 +5,7 @@ use crate::tools::TELEMETRY_PREVIEW_MAX_LINES; use crate::tools::TELEMETRY_PREVIEW_TRUNCATION_NOTICE; use crate::turn_diff_tracker::TurnDiffTracker; use codex_protocol::mcp::CallToolResult; -use codex_protocol::models::FunctionCallOutputContentItem; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ShellToolCallParams; @@ -58,10 +58,9 @@ impl ToolPayload { #[derive(Clone)] pub enum ToolOutput { Function { - // Plain text representation of the tool output. - content: String, - // Some tool calls such as MCP calls may return structured content that can get parsed into an array of polymorphic content items. - content_items: Option>, + // Canonical output body for function-style tools. This may be plain text + // or structured content items. + body: FunctionCallOutputBody, success: Option, }, Mcp { @@ -72,7 +71,9 @@ pub enum ToolOutput { impl ToolOutput { pub fn log_preview(&self) -> String { match self { - ToolOutput::Function { content, .. } => telemetry_preview(content), + ToolOutput::Function { body, .. } => { + telemetry_preview(&body.to_text().unwrap_or_default()) + } ToolOutput::Mcp { result } => format!("{result:?}"), } } @@ -86,27 +87,28 @@ impl ToolOutput { pub fn into_response(self, call_id: &str, payload: &ToolPayload) -> ResponseInputItem { match self { - ToolOutput::Function { - content, - content_items, - success, - } => { + ToolOutput::Function { body, success } => { + // `custom_tool_call` is the Responses API item type for freeform + // tools (`ToolSpec::Freeform`, e.g. freeform `apply_patch`). + // Those payloads must round-trip as `custom_tool_call_output` + // with plain string output. if matches!(payload, ToolPayload::Custom { .. }) { - ResponseInputItem::CustomToolCallOutput { + // Freeform/custom tools (`custom_tool_call`) use the custom + // output wire shape and remain string-only. + return ResponseInputItem::CustomToolCallOutput { call_id: call_id.to_string(), - output: content, - } - } else { - ResponseInputItem::FunctionCallOutput { - call_id: call_id.to_string(), - output: FunctionCallOutputPayload { - content, - content_items, - success, - }, - } + output: body.to_text().unwrap_or_default(), + }; + } + + // Function-style outputs (JSON function tools, including dynamic + // tools and MCP adaptation) preserve the exact body shape. + ResponseInputItem::FunctionCallOutput { + call_id: call_id.to_string(), + output: FunctionCallOutputPayload { body, success }, } } + // Direct MCP response path for MCP tool result envelopes. ToolOutput::Mcp { result } => ResponseInputItem::McpToolCallOutput { call_id: call_id.to_string(), result, @@ -158,6 +160,7 @@ fn telemetry_preview(content: &str) -> String { #[cfg(test)] mod tests { use super::*; + use codex_protocol::models::FunctionCallOutputContentItem; use pretty_assertions::assert_eq; #[test] @@ -166,8 +169,7 @@ mod tests { input: "patch".to_string(), }; let response = ToolOutput::Function { - content: "patched".to_string(), - content_items: None, + body: FunctionCallOutputBody::Text("patched".to_string()), success: Some(true), } .into_response("call-42", &payload); @@ -187,8 +189,7 @@ mod tests { arguments: "{}".to_string(), }; let response = ToolOutput::Function { - content: "ok".to_string(), - content_items: None, + body: FunctionCallOutputBody::Text("ok".to_string()), success: Some(true), } .into_response("fn-1", &payload); @@ -196,14 +197,58 @@ mod tests { match response { ResponseInputItem::FunctionCallOutput { call_id, output } => { assert_eq!(call_id, "fn-1"); - assert_eq!(output.content, "ok"); - assert!(output.content_items.is_none()); + assert_eq!(output.text_content(), Some("ok")); + assert!(output.content_items().is_none()); assert_eq!(output.success, Some(true)); } other => panic!("expected FunctionCallOutput, got {other:?}"), } } + #[test] + fn custom_tool_calls_can_derive_text_from_content_items() { + let payload = ToolPayload::Custom { + input: "patch".to_string(), + }; + let response = ToolOutput::Function { + body: FunctionCallOutputBody::ContentItems(vec![ + FunctionCallOutputContentItem::InputText { + text: "line 1".to_string(), + }, + FunctionCallOutputContentItem::InputImage { + image_url: "data:image/png;base64,AAA".to_string(), + }, + FunctionCallOutputContentItem::InputText { + text: "line 2".to_string(), + }, + ]), + success: Some(true), + } + .into_response("call-99", &payload); + + match response { + ResponseInputItem::CustomToolCallOutput { call_id, output } => { + assert_eq!(call_id, "call-99"); + assert_eq!(output, "line 1\nline 2"); + } + other => panic!("expected CustomToolCallOutput, got {other:?}"), + } + } + + #[test] + fn log_preview_uses_content_items_when_plain_text_is_missing() { + let output = ToolOutput::Function { + body: FunctionCallOutputBody::ContentItems(vec![ + FunctionCallOutputContentItem::InputText { + text: "preview".to_string(), + }, + ]), + success: Some(true), + }; + + assert_eq!(output.log_preview(), "preview"); + } + #[test] fn telemetry_preview_returns_original_within_limits() { let content = "short output"; diff --git a/codex-rs/core/src/tools/handlers/apply_patch.rs b/codex-rs/core/src/tools/handlers/apply_patch.rs index 46723decf6..aced569ce4 100644 --- a/codex-rs/core/src/tools/handlers/apply_patch.rs +++ b/codex-rs/core/src/tools/handlers/apply_patch.rs @@ -1,3 +1,4 @@ +use codex_protocol::models::FunctionCallOutputBody; use std::collections::BTreeMap; use std::path::Path; @@ -109,8 +110,7 @@ impl ToolHandler for ApplyPatchHandler { InternalApplyPatchInvocation::Output(item) => { let content = item?; Ok(ToolOutput::Function { - content, - content_items: None, + body: FunctionCallOutputBody::Text(content), success: Some(true), }) } @@ -155,8 +155,7 @@ impl ToolHandler for ApplyPatchHandler { ); let content = emitter.finish(event_ctx, out).await?; Ok(ToolOutput::Function { - content, - content_items: None, + body: FunctionCallOutputBody::Text(content), success: Some(true), }) } @@ -205,8 +204,7 @@ pub(crate) async fn intercept_apply_patch( InternalApplyPatchInvocation::Output(item) => { let content = item?; Ok(Some(ToolOutput::Function { - content, - content_items: None, + body: FunctionCallOutputBody::Text(content), success: Some(true), })) } @@ -242,8 +240,7 @@ pub(crate) async fn intercept_apply_patch( ToolEventCtx::new(session, turn, call_id, tracker.as_ref().copied()); let content = emitter.finish(event_ctx, out).await?; Ok(Some(ToolOutput::Function { - content, - content_items: None, + body: FunctionCallOutputBody::Text(content), success: Some(true), })) } diff --git a/codex-rs/core/src/tools/handlers/collab.rs b/codex-rs/core/src/tools/handlers/collab.rs index 61ccc1932e..e6f0c3c959 100644 --- a/codex-rs/core/src/tools/handlers/collab.rs +++ b/codex-rs/core/src/tools/handlers/collab.rs @@ -15,6 +15,7 @@ use crate::tools::registry::ToolKind; use async_trait::async_trait; use codex_protocol::ThreadId; use codex_protocol::models::BaseInstructions; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::protocol::CollabAgentInteractionBeginEvent; use codex_protocol::protocol::CollabAgentInteractionEndEvent; use codex_protocol::protocol::CollabAgentSpawnBeginEvent; @@ -114,7 +115,7 @@ mod spawn { "Empty message can't be sent to an agent".to_string(), )); } - let session_source = turn.client.get_session_source(); + let session_source = turn.session_source.clone(); let child_depth = next_thread_spawn_depth(&session_source); if exceeds_thread_spawn_depth_limit(child_depth) { return Err(FunctionCallError::RespondToModel( @@ -184,9 +185,8 @@ mod spawn { })?; Ok(ToolOutput::Function { - content, + body: FunctionCallOutputBody::Text(content), success: Some(true), - content_items: None, }) } } @@ -273,9 +273,8 @@ mod send_input { })?; Ok(ToolOutput::Function { - content, + body: FunctionCallOutputBody::Text(content), success: Some(true), - content_items: None, }) } } @@ -441,9 +440,8 @@ mod wait { })?; Ok(ToolOutput::Function { - content, + body: FunctionCallOutputBody::Text(content), success: None, - content_items: None, }) } @@ -552,9 +550,8 @@ pub mod close_agent { })?; Ok(ToolOutput::Function { - content, + body: FunctionCallOutputBody::Text(content), success: Some(true), - content_items: None, }) } } @@ -593,13 +590,13 @@ fn build_agent_spawn_config( turn: &TurnContext, child_depth: i32, ) -> Result { - let base_config = turn.client.config(); + let base_config = turn.config.clone(); let mut config = (*base_config).clone(); config.base_instructions = Some(base_instructions.text.clone()); - config.model = Some(turn.client.get_model()); - config.model_provider = turn.client.get_provider(); - config.model_reasoning_effort = turn.client.get_reasoning_effort(); - config.model_reasoning_summary = turn.client.get_reasoning_summary(); + config.model = Some(turn.model_info.slug.clone()); + config.model_provider = turn.provider.clone(); + config.model_reasoning_effort = turn.reasoning_effort; + config.model_reasoning_summary = turn.reasoning_summary; config.developer_instructions = turn.developer_instructions.clone(); config.compact_prompt = turn.compact_prompt.clone(); config.shell_environment_policy = turn.shell_environment_policy.clone(); @@ -633,7 +630,6 @@ mod tests { use crate::ThreadManager; use crate::agent::MAX_THREAD_SPAWN_DEPTH; use crate::built_in_model_providers; - use crate::client::ModelClient; use crate::codex::make_session_and_context; use crate::config::types::ShellEnvironmentPolicy; use crate::function_tool::FunctionCallError; @@ -767,22 +763,10 @@ mod tests { let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let session_source = SessionSource::SubAgent(SubAgentSource::ThreadSpawn { + turn.session_source = SessionSource::SubAgent(SubAgentSource::ThreadSpawn { parent_thread_id: session.conversation_id, depth: MAX_THREAD_SPAWN_DEPTH, }); - turn.client = ModelClient::new( - turn.client.config(), - Some(session.services.auth_manager.clone()), - turn.client.get_model_info(), - turn.client.get_otel_manager(), - turn.client.get_provider(), - turn.client.get_reasoning_effort(), - turn.client.get_reasoning_summary(), - session.conversation_id, - session_source, - session.services.transport_manager.clone(), - ); let invocation = invocation( Arc::new(session), @@ -865,7 +849,7 @@ mod tests { let (mut session, turn) = make_session_and_context().await; let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let config = turn.client.config().as_ref().clone(); + let config = turn.config.as_ref().clone(); let thread = manager.start_thread(config).await.expect("start thread"); let agent_id = thread.thread_id; let invocation = invocation( @@ -983,7 +967,9 @@ mod tests { .await .expect("wait should succeed"); let ToolOutput::Function { - content, success, .. + body: FunctionCallOutputBody::Text(content), + success, + .. } = output else { panic!("expected function output"); @@ -1008,7 +994,7 @@ mod tests { let (mut session, turn) = make_session_and_context().await; let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let config = turn.client.config().as_ref().clone(); + let config = turn.config.as_ref().clone(); let thread = manager.start_thread(config).await.expect("start thread"); let agent_id = thread.thread_id; let invocation = invocation( @@ -1025,7 +1011,9 @@ mod tests { .await .expect("wait should succeed"); let ToolOutput::Function { - content, success, .. + body: FunctionCallOutputBody::Text(content), + success, + .. } = output else { panic!("expected function output"); @@ -1053,7 +1041,7 @@ mod tests { let (mut session, turn) = make_session_and_context().await; let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let config = turn.client.config().as_ref().clone(); + let config = turn.config.as_ref().clone(); let thread = manager.start_thread(config).await.expect("start thread"); let agent_id = thread.thread_id; let invocation = invocation( @@ -1084,7 +1072,7 @@ mod tests { let (mut session, turn) = make_session_and_context().await; let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let config = turn.client.config().as_ref().clone(); + let config = turn.config.as_ref().clone(); let thread = manager.start_thread(config).await.expect("start thread"); let agent_id = thread.thread_id; let mut status_rx = manager @@ -1116,7 +1104,9 @@ mod tests { .await .expect("wait should succeed"); let ToolOutput::Function { - content, success, .. + body: FunctionCallOutputBody::Text(content), + success, + .. } = output else { panic!("expected function output"); @@ -1138,7 +1128,7 @@ mod tests { let (mut session, turn) = make_session_and_context().await; let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let config = turn.client.config().as_ref().clone(); + let config = turn.config.as_ref().clone(); let thread = manager.start_thread(config).await.expect("start thread"); let agent_id = thread.thread_id; let status_before = manager.agent_control().get_status(agent_id).await; @@ -1154,7 +1144,9 @@ mod tests { .await .expect("close_agent should succeed"); let ToolOutput::Function { - content, success, .. + body: FunctionCallOutputBody::Text(content), + success, + .. } = output else { panic!("expected function output"); @@ -1176,6 +1168,37 @@ mod tests { #[tokio::test] async fn build_agent_spawn_config_uses_turn_context_values() { + fn pick_allowed_approval_policy( + constraint: &crate::config::Constrained, + base: AskForApproval, + ) -> AskForApproval { + let candidates = [ + AskForApproval::Never, + AskForApproval::UnlessTrusted, + AskForApproval::OnRequest, + AskForApproval::OnFailure, + ]; + candidates + .into_iter() + .find(|candidate| *candidate != base && constraint.can_set(candidate).is_ok()) + .unwrap_or(base) + } + + fn pick_allowed_sandbox_policy( + constraint: &crate::config::Constrained, + base: SandboxPolicy, + ) -> SandboxPolicy { + let candidates = [ + SandboxPolicy::new_read_only_policy(), + SandboxPolicy::new_workspace_write_policy(), + SandboxPolicy::DangerFullAccess, + ]; + candidates + .into_iter() + .find(|candidate| *candidate != base && constraint.can_set(candidate).is_ok()) + .unwrap_or(base) + } + let (_session, mut turn) = make_session_and_context().await; let base_instructions = BaseInstructions { text: "base".to_string(), @@ -1189,16 +1212,22 @@ mod tests { let temp_dir = tempfile::tempdir().expect("temp dir"); turn.cwd = temp_dir.path().to_path_buf(); turn.codex_linux_sandbox_exe = Some(PathBuf::from("/bin/echo")); - turn.approval_policy = AskForApproval::Never; - turn.sandbox_policy = SandboxPolicy::DangerFullAccess; + turn.approval_policy = pick_allowed_approval_policy( + &turn.config.approval_policy, + *turn.config.approval_policy.get(), + ); + turn.sandbox_policy = pick_allowed_sandbox_policy( + &turn.config.sandbox_policy, + turn.config.sandbox_policy.get().clone(), + ); let config = build_agent_spawn_config(&base_instructions, &turn, 0).expect("spawn config"); - let mut expected = (*turn.client.config()).clone(); + let mut expected = (*turn.config).clone(); expected.base_instructions = Some(base_instructions.text); - expected.model = Some(turn.client.get_model()); - expected.model_provider = turn.client.get_provider(); - expected.model_reasoning_effort = turn.client.get_reasoning_effort(); - expected.model_reasoning_summary = turn.client.get_reasoning_summary(); + expected.model = Some(turn.model_info.slug.clone()); + expected.model_provider = turn.provider.clone(); + expected.model_reasoning_effort = turn.reasoning_effort; + expected.model_reasoning_summary = turn.reasoning_summary; expected.developer_instructions = turn.developer_instructions.clone(); expected.compact_prompt = turn.compact_prompt.clone(); expected.shell_environment_policy = turn.shell_environment_policy.clone(); @@ -1217,24 +1246,11 @@ mod tests { #[tokio::test] async fn build_agent_spawn_config_preserves_base_user_instructions() { - let (session, mut turn) = make_session_and_context().await; - let session_source = turn.client.get_session_source(); - let mut base_config = (*turn.client.config()).clone(); + let (_session, mut turn) = make_session_and_context().await; + let mut base_config = (*turn.config).clone(); base_config.user_instructions = Some("base-user".to_string()); turn.user_instructions = Some("resolved-user".to_string()); - let transport_manager = turn.client.transport_manager(); - turn.client = ModelClient::new( - Arc::new(base_config.clone()), - Some(session.services.auth_manager.clone()), - turn.client.get_model_info(), - turn.client.get_otel_manager(), - turn.client.get_provider(), - turn.client.get_reasoning_effort(), - turn.client.get_reasoning_summary(), - session.conversation_id, - session_source, - transport_manager, - ); + turn.config = Arc::new(base_config.clone()); let base_instructions = BaseInstructions { text: "base".to_string(), }; diff --git a/codex-rs/core/src/tools/handlers/dynamic.rs b/codex-rs/core/src/tools/handlers/dynamic.rs index a68c70b98d..51dc8db56f 100644 --- a/codex-rs/core/src/tools/handlers/dynamic.rs +++ b/codex-rs/core/src/tools/handlers/dynamic.rs @@ -10,6 +10,8 @@ use crate::tools::registry::ToolKind; use async_trait::async_trait; use codex_protocol::dynamic_tools::DynamicToolCallRequest; use codex_protocol::dynamic_tools::DynamicToolResponse; +use codex_protocol::models::FunctionCallOutputBody; +use codex_protocol::models::FunctionCallOutputContentItem; use codex_protocol::protocol::EventMsg; use serde_json::Value; use tokio::sync::oneshot; @@ -55,10 +57,19 @@ impl ToolHandler for DynamicToolHandler { ) })?; + let DynamicToolResponse { + content_items, + success, + } = response; + let body = content_items + .into_iter() + .map(FunctionCallOutputContentItem::from) + .collect::>(); + let body = FunctionCallOutputBody::ContentItems(body); + Ok(ToolOutput::Function { - content: response.output, - content_items: None, - success: Some(response.success), + body, + success: Some(success), }) } } diff --git a/codex-rs/core/src/tools/handlers/get_memory.rs b/codex-rs/core/src/tools/handlers/get_memory.rs new file mode 100644 index 0000000000..df2929b88a --- /dev/null +++ b/codex-rs/core/src/tools/handlers/get_memory.rs @@ -0,0 +1,72 @@ +use crate::function_tool::FunctionCallError; +use crate::state_db; +use crate::tools::context::ToolInvocation; +use crate::tools::context::ToolOutput; +use crate::tools::context::ToolPayload; +use crate::tools::handlers::parse_arguments; +use crate::tools::registry::ToolHandler; +use crate::tools::registry::ToolKind; +use async_trait::async_trait; +use codex_protocol::ThreadId; +use codex_protocol::models::FunctionCallOutputBody; +use serde::Deserialize; +use serde_json::json; + +pub struct GetMemoryHandler; + +#[derive(Deserialize)] +struct GetMemoryArgs { + memory_id: String, +} + +#[async_trait] +impl ToolHandler for GetMemoryHandler { + fn kind(&self) -> ToolKind { + ToolKind::Function + } + + async fn handle(&self, invocation: ToolInvocation) -> Result { + let ToolInvocation { + session, payload, .. + } = invocation; + + let arguments = match payload { + ToolPayload::Function { arguments } => arguments, + _ => { + return Err(FunctionCallError::RespondToModel( + "get_memory handler received unsupported payload".to_string(), + )); + } + }; + + let args: GetMemoryArgs = parse_arguments(&arguments)?; + let thread_id = ThreadId::from_string(args.memory_id.as_str()).map_err(|err| { + FunctionCallError::RespondToModel(format!("memory_id must be a valid thread id: {err}")) + })?; + + let state_db_ctx = session.state_db(); + let memory = + state_db::get_thread_memory(state_db_ctx.as_deref(), thread_id, "get_memory_tool") + .await + .ok_or_else(|| { + FunctionCallError::RespondToModel(format!( + "memory not found for memory_id={}", + args.memory_id + )) + })?; + + let content = serde_json::to_string_pretty(&json!({ + "memory_id": args.memory_id, + "trace_summary": memory.trace_summary, + "memory_summary": memory.memory_summary, + })) + .map_err(|err| { + FunctionCallError::Fatal(format!("failed to serialize memory payload: {err}")) + })?; + + Ok(ToolOutput::Function { + body: FunctionCallOutputBody::Text(content), + success: Some(true), + }) + } +} diff --git a/codex-rs/core/src/tools/handlers/grep_files.rs b/codex-rs/core/src/tools/handlers/grep_files.rs index a3e89af6a8..9fbc6c17a3 100644 --- a/codex-rs/core/src/tools/handlers/grep_files.rs +++ b/codex-rs/core/src/tools/handlers/grep_files.rs @@ -1,3 +1,4 @@ +use codex_protocol::models::FunctionCallOutputBody; use std::path::Path; use std::time::Duration; @@ -86,14 +87,12 @@ impl ToolHandler for GrepFilesHandler { if search_results.is_empty() { Ok(ToolOutput::Function { - content: "No matches found.".to_string(), - content_items: None, + body: FunctionCallOutputBody::Text("No matches found.".to_string()), success: Some(false), }) } else { Ok(ToolOutput::Function { - content: search_results.join("\n"), - content_items: None, + body: FunctionCallOutputBody::Text(search_results.join("\n")), success: Some(true), }) } diff --git a/codex-rs/core/src/tools/handlers/list_dir.rs b/codex-rs/core/src/tools/handlers/list_dir.rs index a06fca3d14..5535ce0ba6 100644 --- a/codex-rs/core/src/tools/handlers/list_dir.rs +++ b/codex-rs/core/src/tools/handlers/list_dir.rs @@ -1,3 +1,4 @@ +use codex_protocol::models::FunctionCallOutputBody; use std::collections::VecDeque; use std::ffi::OsStr; use std::fs::FileType; @@ -102,8 +103,7 @@ impl ToolHandler for ListDirHandler { output.push(format!("Absolute path: {}", path.display())); output.extend(entries); Ok(ToolOutput::Function { - content: output.join("\n"), - content_items: None, + body: FunctionCallOutputBody::Text(output.join("\n")), success: Some(true), }) } diff --git a/codex-rs/core/src/tools/handlers/mcp.rs b/codex-rs/core/src/tools/handlers/mcp.rs index 5138b3cd0d..8ea46b4ed8 100644 --- a/codex-rs/core/src/tools/handlers/mcp.rs +++ b/codex-rs/core/src/tools/handlers/mcp.rs @@ -8,6 +8,7 @@ use crate::tools::context::ToolOutput; use crate::tools::context::ToolPayload; use crate::tools::registry::ToolHandler; use crate::tools::registry::ToolKind; +use codex_protocol::models::ResponseInputItem; pub struct McpHandler; @@ -53,20 +54,11 @@ impl ToolHandler for McpHandler { .await; match response { - codex_protocol::models::ResponseInputItem::McpToolCallOutput { result, .. } => { - Ok(ToolOutput::Mcp { result }) - } - codex_protocol::models::ResponseInputItem::FunctionCallOutput { output, .. } => { - let codex_protocol::models::FunctionCallOutputPayload { - content, - content_items, - success, - } = output; - Ok(ToolOutput::Function { - content, - content_items, - success, - }) + ResponseInputItem::McpToolCallOutput { result, .. } => Ok(ToolOutput::Mcp { result }), + ResponseInputItem::FunctionCallOutput { output, .. } => { + let success = output.success; + let body = output.body; + Ok(ToolOutput::Function { body, success }) } _ => Err(FunctionCallError::RespondToModel( "mcp handler received unexpected response variant".to_string(), diff --git a/codex-rs/core/src/tools/handlers/mcp_resource.rs b/codex-rs/core/src/tools/handlers/mcp_resource.rs index df421bd6d3..fe541197cd 100644 --- a/codex-rs/core/src/tools/handlers/mcp_resource.rs +++ b/codex-rs/core/src/tools/handlers/mcp_resource.rs @@ -1,3 +1,4 @@ +use codex_protocol::models::FunctionCallOutputBody; use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; @@ -296,12 +297,10 @@ async fn handle_list_resources( match payload_result { Ok(payload) => match serialize_function_output(payload) { Ok(output) => { - let ToolOutput::Function { - content, success, .. - } = &output - else { + let ToolOutput::Function { body, success } = &output else { unreachable!("MCP resource handler should return function output"); }; + let content = body.to_text().unwrap_or_default(); let duration = start.elapsed(); emit_tool_call_end( &session, @@ -309,7 +308,7 @@ async fn handle_list_resources( &call_id, invocation, duration, - Ok(call_tool_result_from_content(content, *success)), + Ok(call_tool_result_from_content(&content, *success)), ) .await; Ok(output) @@ -405,12 +404,10 @@ async fn handle_list_resource_templates( match payload_result { Ok(payload) => match serialize_function_output(payload) { Ok(output) => { - let ToolOutput::Function { - content, success, .. - } = &output - else { + let ToolOutput::Function { body, success } = &output else { unreachable!("MCP resource handler should return function output"); }; + let content = body.to_text().unwrap_or_default(); let duration = start.elapsed(); emit_tool_call_end( &session, @@ -418,7 +415,7 @@ async fn handle_list_resource_templates( &call_id, invocation, duration, - Ok(call_tool_result_from_content(content, *success)), + Ok(call_tool_result_from_content(&content, *success)), ) .await; Ok(output) @@ -494,12 +491,10 @@ async fn handle_read_resource( match payload_result { Ok(payload) => match serialize_function_output(payload) { Ok(output) => { - let ToolOutput::Function { - content, success, .. - } = &output - else { + let ToolOutput::Function { body, success } = &output else { unreachable!("MCP resource handler should return function output"); }; + let content = body.to_text().unwrap_or_default(); let duration = start.elapsed(); emit_tool_call_end( &session, @@ -507,7 +502,7 @@ async fn handle_read_resource( &call_id, invocation, duration, - Ok(call_tool_result_from_content(content, *success)), + Ok(call_tool_result_from_content(&content, *success)), ) .await; Ok(output) @@ -622,8 +617,7 @@ where })?; Ok(ToolOutput::Function { - content, - content_items: None, + body: FunctionCallOutputBody::Text(content), success: Some(true), }) } diff --git a/codex-rs/core/src/tools/handlers/mod.rs b/codex-rs/core/src/tools/handlers/mod.rs index dda4760bd7..d8ec887162 100644 --- a/codex-rs/core/src/tools/handlers/mod.rs +++ b/codex-rs/core/src/tools/handlers/mod.rs @@ -1,6 +1,7 @@ pub mod apply_patch; pub(crate) mod collab; mod dynamic; +mod get_memory; mod grep_files; mod list_dir; mod mcp; @@ -20,6 +21,7 @@ use crate::function_tool::FunctionCallError; pub use apply_patch::ApplyPatchHandler; pub use collab::CollabHandler; pub use dynamic::DynamicToolHandler; +pub use get_memory::GetMemoryHandler; pub use grep_files::GrepFilesHandler; pub use list_dir::ListDirHandler; pub use mcp::McpHandler; diff --git a/codex-rs/core/src/tools/handlers/plan.rs b/codex-rs/core/src/tools/handlers/plan.rs index 88e0b0dc27..2b43429cc8 100644 --- a/codex-rs/core/src/tools/handlers/plan.rs +++ b/codex-rs/core/src/tools/handlers/plan.rs @@ -11,6 +11,7 @@ use crate::tools::registry::ToolKind; use crate::tools::spec::JsonSchema; use async_trait::async_trait; use codex_protocol::config_types::ModeKind; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::plan_tool::UpdatePlanArgs; use codex_protocol::protocol::EventMsg; use std::collections::BTreeMap; @@ -88,8 +89,7 @@ impl ToolHandler for PlanHandler { handle_update_plan(session.as_ref(), turn.as_ref(), arguments, call_id).await?; Ok(ToolOutput::Function { - content, - content_items: None, + body: FunctionCallOutputBody::Text(content), success: Some(true), }) } diff --git a/codex-rs/core/src/tools/handlers/read_file.rs b/codex-rs/core/src/tools/handlers/read_file.rs index eb5ce647ce..59c6ced7f5 100644 --- a/codex-rs/core/src/tools/handlers/read_file.rs +++ b/codex-rs/core/src/tools/handlers/read_file.rs @@ -1,3 +1,4 @@ +use codex_protocol::models::FunctionCallOutputBody; use std::collections::VecDeque; use std::path::PathBuf; @@ -146,8 +147,7 @@ impl ToolHandler for ReadFileHandler { } }; Ok(ToolOutput::Function { - content: collected.join("\n"), - content_items: None, + body: FunctionCallOutputBody::Text(collected.join("\n")), success: Some(true), }) } diff --git a/codex-rs/core/src/tools/handlers/request_user_input.rs b/codex-rs/core/src/tools/handlers/request_user_input.rs index 6d014755b5..134294d428 100644 --- a/codex-rs/core/src/tools/handlers/request_user_input.rs +++ b/codex-rs/core/src/tools/handlers/request_user_input.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use codex_protocol::models::FunctionCallOutputBody; use crate::function_tool::FunctionCallError; use crate::tools::context::ToolInvocation; @@ -8,27 +9,15 @@ use crate::tools::handlers::parse_arguments; use crate::tools::registry::ToolHandler; use crate::tools::registry::ToolKind; use codex_protocol::config_types::ModeKind; +use codex_protocol::config_types::TUI_VISIBLE_COLLABORATION_MODES; use codex_protocol::request_user_input::RequestUserInputArgs; -const REQUEST_USER_INPUT_ALLOWED_MODES: [ModeKind; 1] = [ModeKind::Plan]; - -fn request_user_input_mode_name(mode: ModeKind) -> &'static str { - match mode { - ModeKind::Plan => "Plan", - ModeKind::Default => "Default", - ModeKind::Execute => "Execute", - ModeKind::PairProgramming => "Pair Programming", - } -} - fn format_allowed_modes() -> String { - let mut mode_names = Vec::with_capacity(REQUEST_USER_INPUT_ALLOWED_MODES.len()); - for mode in REQUEST_USER_INPUT_ALLOWED_MODES { - let name = request_user_input_mode_name(mode); - if !mode_names.contains(&name) { - mode_names.push(name); - } - } + let mode_names: Vec<&str> = TUI_VISIBLE_COLLABORATION_MODES + .into_iter() + .filter(|mode| mode.allows_request_user_input()) + .map(ModeKind::display_name) + .collect(); match mode_names.as_slice() { [] => "no modes".to_string(), @@ -38,15 +27,11 @@ fn format_allowed_modes() -> String { } } -fn request_user_input_is_available_in_mode(mode: ModeKind) -> bool { - REQUEST_USER_INPUT_ALLOWED_MODES.contains(&mode) -} - pub(crate) fn request_user_input_unavailable_message(mode: ModeKind) -> Option { - if request_user_input_is_available_in_mode(mode) { + if mode.allows_request_user_input() { None } else { - let mode_name = request_user_input_mode_name(mode); + let mode_name = mode.display_name(); Some(format!( "request_user_input is unavailable in {mode_name} mode" )) @@ -120,8 +105,7 @@ impl ToolHandler for RequestUserInputHandler { })?; Ok(ToolOutput::Function { - content, - content_items: None, + body: FunctionCallOutputBody::Text(content), success: Some(true), }) } @@ -134,22 +118,10 @@ mod tests { #[test] fn request_user_input_mode_availability_is_plan_only() { - assert_eq!( - request_user_input_is_available_in_mode(ModeKind::Plan), - true - ); - assert_eq!( - request_user_input_is_available_in_mode(ModeKind::Default), - false - ); - assert_eq!( - request_user_input_is_available_in_mode(ModeKind::Execute), - false - ); - assert_eq!( - request_user_input_is_available_in_mode(ModeKind::PairProgramming), - false - ); + assert!(ModeKind::Plan.allows_request_user_input()); + assert!(!ModeKind::Default.allows_request_user_input()); + assert!(!ModeKind::Execute.allows_request_user_input()); + assert!(!ModeKind::PairProgramming.allows_request_user_input()); } #[test] diff --git a/codex-rs/core/src/tools/handlers/shell.rs b/codex-rs/core/src/tools/handlers/shell.rs index f62caea556..cd1aa9056a 100644 --- a/codex-rs/core/src/tools/handlers/shell.rs +++ b/codex-rs/core/src/tools/handlers/shell.rs @@ -1,5 +1,6 @@ use async_trait::async_trait; use codex_protocol::ThreadId; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::ShellCommandToolCallParams; use codex_protocol::models::ShellToolCallParams; use std::sync::Arc; @@ -329,8 +330,7 @@ impl ShellHandler { let event_ctx = ToolEventCtx::new(session.as_ref(), turn.as_ref(), &call_id, None); let content = emitter.finish(event_ctx, out).await?; Ok(ToolOutput::Function { - content, - content_items: None, + body: FunctionCallOutputBody::Text(content), success: Some(true), }) } diff --git a/codex-rs/core/src/tools/handlers/test_sync.rs b/codex-rs/core/src/tools/handlers/test_sync.rs index 643cb464fa..4d8fe1025b 100644 --- a/codex-rs/core/src/tools/handlers/test_sync.rs +++ b/codex-rs/core/src/tools/handlers/test_sync.rs @@ -1,3 +1,4 @@ +use codex_protocol::models::FunctionCallOutputBody; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::sync::Arc; @@ -91,8 +92,7 @@ impl ToolHandler for TestSyncHandler { } Ok(ToolOutput::Function { - content: "ok".to_string(), - content_items: None, + body: FunctionCallOutputBody::Text("ok".to_string()), success: Some(true), }) } diff --git a/codex-rs/core/src/tools/handlers/unified_exec.rs b/codex-rs/core/src/tools/handlers/unified_exec.rs index 2e331bae59..1eb51dd733 100644 --- a/codex-rs/core/src/tools/handlers/unified_exec.rs +++ b/codex-rs/core/src/tools/handlers/unified_exec.rs @@ -18,6 +18,7 @@ use crate::unified_exec::UnifiedExecProcessManager; use crate::unified_exec::UnifiedExecResponse; use crate::unified_exec::WriteStdinRequest; use async_trait::async_trait; +use codex_protocol::models::FunctionCallOutputBody; use serde::Deserialize; use std::path::PathBuf; use std::sync::Arc; @@ -238,8 +239,7 @@ impl ToolHandler for UnifiedExecHandler { let content = format_response(&response); Ok(ToolOutput::Function { - content, - content_items: None, + body: FunctionCallOutputBody::Text(content), success: Some(true), }) } diff --git a/codex-rs/core/src/tools/handlers/view_image.rs b/codex-rs/core/src/tools/handlers/view_image.rs index 87dd7207b1..fe62522180 100644 --- a/codex-rs/core/src/tools/handlers/view_image.rs +++ b/codex-rs/core/src/tools/handlers/view_image.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use codex_protocol::models::FunctionCallOutputBody; use serde::Deserialize; use tokio::fs; @@ -92,8 +93,7 @@ impl ToolHandler for ViewImageHandler { .await; Ok(ToolOutput::Function { - content: "attached local image path".to_string(), - content_items: None, + body: FunctionCallOutputBody::Text("attached local image path".to_string()), success: Some(true), }) } diff --git a/codex-rs/core/src/tools/orchestrator.rs b/codex-rs/core/src/tools/orchestrator.rs index e9fdd6208b..381f8ce136 100644 --- a/codex-rs/core/src/tools/orchestrator.rs +++ b/codex-rs/core/src/tools/orchestrator.rs @@ -8,6 +8,7 @@ retry without sandbox on denial (no re‑approval thanks to caching). use crate::error::CodexErr; use crate::error::SandboxErr; use crate::exec::ExecToolCallOutput; +use crate::features::Feature; use crate::sandboxing::SandboxManager; use crate::tools::sandboxing::ApprovalCtx; use crate::tools::sandboxing::ExecApprovalRequirement; @@ -43,7 +44,7 @@ impl ToolOrchestrator { where T: ToolRuntime, { - let otel = turn_ctx.client.get_otel_manager(); + let otel = turn_ctx.otel_manager.clone(); let otel_tn = &tool_ctx.tool_name; let otel_ci = &tool_ctx.call_id; let otel_user = ToolDecisionSource::User; @@ -97,12 +98,14 @@ impl ToolOrchestrator { // Platform-specific flag gating is handled by SandboxManager::select_initial // via crate::safety::get_platform_sandbox(..). + let use_linux_sandbox_bwrap = turn_ctx.features.enabled(Feature::UseLinuxSandboxBwrap); let initial_attempt = SandboxAttempt { sandbox: initial_sandbox, policy: &turn_ctx.sandbox_policy, manager: &self.sandbox, sandbox_cwd: &turn_ctx.cwd, codex_linux_sandbox_exe: turn_ctx.codex_linux_sandbox_exe.as_ref(), + use_linux_sandbox_bwrap, windows_sandbox_level: turn_ctx.windows_sandbox_level, }; @@ -154,6 +157,7 @@ impl ToolOrchestrator { manager: &self.sandbox, sandbox_cwd: &turn_ctx.cwd, codex_linux_sandbox_exe: None, + use_linux_sandbox_bwrap, windows_sandbox_level: turn_ctx.windows_sandbox_level, }; diff --git a/codex-rs/core/src/tools/parallel.rs b/codex-rs/core/src/tools/parallel.rs index dcd3ae40ad..ca08048bd8 100644 --- a/codex-rs/core/src/tools/parallel.rs +++ b/codex-rs/core/src/tools/parallel.rs @@ -17,6 +17,7 @@ use crate::tools::context::SharedTurnDiffTracker; use crate::tools::context::ToolPayload; use crate::tools::router::ToolCall; use crate::tools::router::ToolRouter; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::models::ResponseInputItem; @@ -119,7 +120,7 @@ impl ToolCallRuntime { _ => ResponseInputItem::FunctionCallOutput { call_id: call.call_id.clone(), output: FunctionCallOutputPayload { - content: Self::abort_message(call, secs), + body: FunctionCallOutputBody::Text(Self::abort_message(call, secs)), ..Default::default() }, }, diff --git a/codex-rs/core/src/tools/registry.rs b/codex-rs/core/src/tools/registry.rs index 72b848e315..3de041c573 100644 --- a/codex-rs/core/src/tools/registry.rs +++ b/codex-rs/core/src/tools/registry.rs @@ -3,11 +3,15 @@ use std::sync::Arc; use std::time::Duration; use crate::client_common::tools::ToolSpec; +use crate::exec::SandboxType; use crate::function_tool::FunctionCallError; +use crate::protocol::SandboxPolicy; +use crate::safety::get_platform_sandbox; use crate::tools::context::ToolInvocation; use crate::tools::context::ToolOutput; use crate::tools::context::ToolPayload; use async_trait::async_trait; +use codex_protocol::config_types::WindowsSandboxLevel; use codex_protocol::models::ResponseInputItem; use codex_utils_readiness::Readiness; use tracing::warn; @@ -70,22 +74,36 @@ impl ToolRegistry { ) -> Result { let tool_name = invocation.tool_name.clone(); let call_id_owned = invocation.call_id.clone(); - let otel = invocation.turn.client.get_otel_manager(); + let otel = invocation.turn.otel_manager.clone(); let payload_for_response = invocation.payload.clone(); let log_payload = payload_for_response.log_payload(); + let metric_tags = [ + ( + "sandbox", + sandbox_tag( + &invocation.turn.sandbox_policy, + invocation.turn.windows_sandbox_level, + ), + ), + ( + "sandbox_policy", + sandbox_policy_tag(&invocation.turn.sandbox_policy), + ), + ]; let handler = match self.handler(tool_name.as_ref()) { Some(handler) => handler, None => { let message = unsupported_tool_call_message(&invocation.payload, tool_name.as_ref()); - otel.tool_result( + otel.tool_result_with_tags( tool_name.as_ref(), &call_id_owned, log_payload.as_ref(), Duration::ZERO, false, &message, + &metric_tags, ); return Err(FunctionCallError::RespondToModel(message)); } @@ -93,13 +111,14 @@ impl ToolRegistry { if !handler.matches_kind(&invocation.payload) { let message = format!("tool {tool_name} invoked with incompatible payload"); - otel.tool_result( + otel.tool_result_with_tags( tool_name.as_ref(), &call_id_owned, log_payload.as_ref(), Duration::ZERO, false, &message, + &metric_tags, ); return Err(FunctionCallError::Fatal(message)); } @@ -107,10 +126,11 @@ impl ToolRegistry { let output_cell = tokio::sync::Mutex::new(None); let result = otel - .log_tool_result( + .log_tool_result_with_tags( tool_name.as_ref(), &call_id_owned, log_payload.as_ref(), + &metric_tags, || { let handler = handler.clone(); let output_cell = &output_cell; @@ -231,3 +251,29 @@ fn unsupported_tool_call_message(payload: &ToolPayload, tool_name: &str) -> Stri _ => format!("unsupported call: {tool_name}"), } } + +fn sandbox_tag(policy: &SandboxPolicy, windows_sandbox_level: WindowsSandboxLevel) -> &'static str { + if matches!(policy, SandboxPolicy::DangerFullAccess) { + return "none"; + } + if matches!(policy, SandboxPolicy::ExternalSandbox { .. }) { + return "external"; + } + if cfg!(target_os = "windows") && matches!(windows_sandbox_level, WindowsSandboxLevel::Elevated) + { + return "windows_elevated"; + } + + get_platform_sandbox(windows_sandbox_level != WindowsSandboxLevel::Disabled) + .map(SandboxType::as_metric_tag) + .unwrap_or("none") +} + +fn sandbox_policy_tag(policy: &SandboxPolicy) -> &'static str { + match policy { + SandboxPolicy::ReadOnly => "read-only", + SandboxPolicy::WorkspaceWrite { .. } => "workspace-write", + SandboxPolicy::DangerFullAccess => "danger-full-access", + SandboxPolicy::ExternalSandbox { .. } => "external-sandbox", + } +} diff --git a/codex-rs/core/src/tools/router.rs b/codex-rs/core/src/tools/router.rs index 51328ccc9f..1eb6190bcb 100644 --- a/codex-rs/core/src/tools/router.rs +++ b/codex-rs/core/src/tools/router.rs @@ -11,6 +11,7 @@ use crate::tools::registry::ToolRegistry; use crate::tools::spec::ToolsConfig; use crate::tools::spec::build_specs; use codex_protocol::dynamic_tools::DynamicToolSpec; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::LocalShellAction; use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ResponseItem; @@ -181,9 +182,8 @@ impl ToolRouter { ResponseInputItem::FunctionCallOutput { call_id, output: codex_protocol::models::FunctionCallOutputPayload { - content: message, + body: FunctionCallOutputBody::Text(message), success: Some(false), - ..Default::default() }, } } diff --git a/codex-rs/core/src/tools/sandboxing.rs b/codex-rs/core/src/tools/sandboxing.rs index a7d2bca62a..d50e592530 100644 --- a/codex-rs/core/src/tools/sandboxing.rs +++ b/codex-rs/core/src/tools/sandboxing.rs @@ -274,6 +274,7 @@ pub(crate) struct SandboxAttempt<'a> { pub(crate) manager: &'a SandboxManager, pub(crate) sandbox_cwd: &'a Path, pub codex_linux_sandbox_exe: Option<&'a std::path::PathBuf>, + pub use_linux_sandbox_bwrap: bool, pub windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel, } @@ -282,14 +283,16 @@ impl<'a> SandboxAttempt<'a> { &self, spec: CommandSpec, ) -> Result { - self.manager.transform( - spec, - self.policy, - self.sandbox, - self.sandbox_cwd, - self.codex_linux_sandbox_exe, - self.windows_sandbox_level, - ) + self.manager + .transform(crate::sandboxing::SandboxTransformRequest { + spec, + policy: self.policy, + sandbox: self.sandbox, + sandbox_policy_cwd: self.sandbox_cwd, + codex_linux_sandbox_exe: self.codex_linux_sandbox_exe, + use_linux_sandbox_bwrap: self.use_linux_sandbox_bwrap, + windows_sandbox_level: self.windows_sandbox_level, + }) } } diff --git a/codex-rs/core/src/tools/spec.rs b/codex-rs/core/src/tools/spec.rs index 8851a157a2..26fccf318a 100644 --- a/codex-rs/core/src/tools/spec.rs +++ b/codex-rs/core/src/tools/spec.rs @@ -31,6 +31,7 @@ pub(crate) struct ToolsConfig { pub web_search_mode: Option, pub collab_tools: bool, pub collaboration_modes_tools: bool, + pub memory_tools: bool, pub request_rule_enabled: bool, pub experimental_supported_tools: Vec, } @@ -51,6 +52,7 @@ impl ToolsConfig { let include_apply_patch_tool = features.enabled(Feature::ApplyPatchFreeform); let include_collab_tools = features.enabled(Feature::Collab); let include_collaboration_modes_tools = features.enabled(Feature::CollaborationModes); + let include_memory_tools = features.enabled(Feature::MemoryTool); let request_rule_enabled = features.enabled(Feature::RequestRule); let shell_type = if !features.enabled(Feature::ShellTool) { @@ -84,6 +86,7 @@ impl ToolsConfig { web_search_mode: *web_search_mode, collab_tools: include_collab_tools, collaboration_modes_tools: include_collaboration_modes_tools, + memory_tools: include_memory_tools, request_rule_enabled, experimental_supported_tools: model_info.experimental_supported_tools.clone(), } @@ -634,6 +637,28 @@ fn create_request_user_input_tool() -> ToolSpec { }) } +fn create_get_memory_tool() -> ToolSpec { + let properties = BTreeMap::from([( + "memory_id".to_string(), + JsonSchema::String { + description: Some( + "Memory ID to fetch. Uses the thread ID as the memory identifier.".to_string(), + ), + }, + )]); + + ToolSpec::Function(ResponsesApiTool { + name: "get_memory".to_string(), + description: "Loads the full stored memory payload for a memory_id.".to_string(), + strict: false, + parameters: JsonSchema::Object { + properties, + required: Some(vec!["memory_id".to_string()]), + additional_properties: Some(false.into()), + }, + }) +} + fn create_close_agent_tool() -> ToolSpec { let mut properties = BTreeMap::new(); properties.insert( @@ -1228,6 +1253,7 @@ pub(crate) fn build_specs( use crate::tools::handlers::ApplyPatchHandler; use crate::tools::handlers::CollabHandler; use crate::tools::handlers::DynamicToolHandler; + use crate::tools::handlers::GetMemoryHandler; use crate::tools::handlers::GrepFilesHandler; use crate::tools::handlers::ListDirHandler; use crate::tools::handlers::McpHandler; @@ -1249,6 +1275,7 @@ pub(crate) fn build_specs( let plan_handler = Arc::new(PlanHandler); let apply_patch_handler = Arc::new(ApplyPatchHandler); let dynamic_tool_handler = Arc::new(DynamicToolHandler); + let get_memory_handler = Arc::new(GetMemoryHandler); let view_image_handler = Arc::new(ViewImageHandler); let mcp_handler = Arc::new(McpHandler); let mcp_resource_handler = Arc::new(McpResourceHandler); @@ -1308,6 +1335,11 @@ pub(crate) fn build_specs( builder.register_handler("request_user_input", request_user_input_handler); } + if config.memory_tools { + builder.push_spec(create_get_memory_tool()); + builder.register_handler("get_memory", get_memory_handler); + } + if let Some(apply_patch_tool_type) = &config.apply_patch_tool_type { match apply_patch_tool_type { ApplyPatchToolType::Freeform => { @@ -1669,6 +1701,33 @@ mod tests { assert_contains_tool_names(&tools, &["request_user_input"]); } + #[test] + fn get_memory_requires_memory_tool_feature() { + let config = test_config(); + let model_info = ModelsManager::construct_model_info_offline("gpt-5-codex", &config); + let mut features = Features::with_defaults(); + features.disable(Feature::MemoryTool); + let tools_config = ToolsConfig::new(&ToolsConfigParams { + model_info: &model_info, + features: &features, + web_search_mode: Some(WebSearchMode::Cached), + }); + let (tools, _) = build_specs(&tools_config, None, &[]).build(); + assert!( + !tools.iter().any(|t| t.spec.name() == "get_memory"), + "get_memory should be disabled when memory_tool feature is off" + ); + + features.enable(Feature::MemoryTool); + let tools_config = ToolsConfig::new(&ToolsConfigParams { + model_info: &model_info, + features: &features, + web_search_mode: Some(WebSearchMode::Cached), + }); + let (tools, _) = build_specs(&tools_config, None, &[]).build(); + assert_contains_tool_names(&tools, &["get_memory"]); + } + fn assert_model_tools( model_slug: &str, features: &Features, @@ -1687,6 +1746,22 @@ mod tests { assert_eq!(&tool_names, &expected_tools,); } + fn assert_default_model_tools( + model_slug: &str, + features: &Features, + web_search_mode: Option, + shell_tool: &'static str, + expected_tail: &[&str], + ) { + let mut expected = if features.enabled(Feature::UnifiedExec) { + vec!["exec_command", "write_stdin"] + } else { + vec![shell_tool] + }; + expected.extend(expected_tail); + assert_model_tools(model_slug, features, web_search_mode, &expected); + } + #[test] fn web_search_mode_cached_sets_external_web_access_false() { let config = test_config(); @@ -1735,12 +1810,12 @@ mod tests { fn test_build_specs_gpt5_codex_default() { let mut features = Features::with_defaults(); features.enable(Feature::CollaborationModes); - assert_model_tools( + assert_default_model_tools( "gpt-5-codex", &features, Some(WebSearchMode::Cached), + "shell_command", &[ - "shell_command", "list_mcp_resources", "list_mcp_resource_templates", "read_mcp_resource", @@ -1757,12 +1832,12 @@ mod tests { fn test_build_specs_gpt51_codex_default() { let mut features = Features::with_defaults(); features.enable(Feature::CollaborationModes); - assert_model_tools( + assert_default_model_tools( "gpt-5.1-codex", &features, Some(WebSearchMode::Cached), + "shell_command", &[ - "shell_command", "list_mcp_resources", "list_mcp_resource_templates", "read_mcp_resource", @@ -1827,12 +1902,12 @@ mod tests { fn test_codex_mini_defaults() { let mut features = Features::with_defaults(); features.enable(Feature::CollaborationModes); - assert_model_tools( + assert_default_model_tools( "codex-mini-latest", &features, Some(WebSearchMode::Cached), + "local_shell", &[ - "local_shell", "list_mcp_resources", "list_mcp_resource_templates", "read_mcp_resource", @@ -1848,12 +1923,12 @@ mod tests { fn test_codex_5_1_mini_defaults() { let mut features = Features::with_defaults(); features.enable(Feature::CollaborationModes); - assert_model_tools( + assert_default_model_tools( "gpt-5.1-codex-mini", &features, Some(WebSearchMode::Cached), + "shell_command", &[ - "shell_command", "list_mcp_resources", "list_mcp_resource_templates", "read_mcp_resource", @@ -1870,12 +1945,12 @@ mod tests { fn test_gpt_5_defaults() { let mut features = Features::with_defaults(); features.enable(Feature::CollaborationModes); - assert_model_tools( + assert_default_model_tools( "gpt-5", &features, Some(WebSearchMode::Cached), + "shell", &[ - "shell", "list_mcp_resources", "list_mcp_resource_templates", "read_mcp_resource", @@ -1891,12 +1966,12 @@ mod tests { fn test_gpt_5_1_defaults() { let mut features = Features::with_defaults(); features.enable(Feature::CollaborationModes); - assert_model_tools( + assert_default_model_tools( "gpt-5.1", &features, Some(WebSearchMode::Cached), + "shell_command", &[ - "shell_command", "list_mcp_resources", "list_mcp_resource_templates", "read_mcp_resource", diff --git a/codex-rs/core/src/transport_manager.rs b/codex-rs/core/src/transport_manager.rs deleted file mode 100644 index 2b5d095000..0000000000 --- a/codex-rs/core/src/transport_manager.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::sync::Arc; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; - -#[derive(Clone, Debug, Default)] -pub struct TransportManager { - disable_websockets: Arc, -} - -impl TransportManager { - pub fn new() -> Self { - Self::default() - } - - pub fn disable_websockets(&self) -> bool { - self.disable_websockets.load(Ordering::Relaxed) - } - - pub fn activate_http_fallback(&self, websocket_enabled: bool) -> bool { - websocket_enabled && !self.disable_websockets.swap(true, Ordering::Relaxed) - } -} diff --git a/codex-rs/core/src/turn_metadata.rs b/codex-rs/core/src/turn_metadata.rs index 58b394a2f2..6d4878c7a3 100644 --- a/codex-rs/core/src/turn_metadata.rs +++ b/codex-rs/core/src/turn_metadata.rs @@ -20,7 +20,7 @@ struct TurnMetadata { workspaces: BTreeMap, } -pub(crate) async fn build_turn_metadata_header(cwd: &Path) -> Option { +pub async fn build_turn_metadata_header(cwd: &Path) -> Option { let repo_root = get_git_repo_root(cwd)?; let (latest_git_commit_hash, associated_remote_urls) = tokio::join!( diff --git a/codex-rs/core/src/user_notification.rs b/codex-rs/core/src/user_notification.rs deleted file mode 100644 index 7bbd1d9564..0000000000 --- a/codex-rs/core/src/user_notification.rs +++ /dev/null @@ -1,87 +0,0 @@ -use serde::Serialize; -use tracing::error; -use tracing::warn; - -#[derive(Debug, Default)] -pub(crate) struct UserNotifier { - notify_command: Option>, -} - -impl UserNotifier { - pub(crate) fn notify(&self, notification: &UserNotification) { - if let Some(notify_command) = &self.notify_command - && !notify_command.is_empty() - { - self.invoke_notify(notify_command, notification) - } - } - - fn invoke_notify(&self, notify_command: &[String], notification: &UserNotification) { - let Ok(json) = serde_json::to_string(¬ification) else { - error!("failed to serialise notification payload"); - return; - }; - - let mut command = std::process::Command::new(¬ify_command[0]); - if notify_command.len() > 1 { - command.args(¬ify_command[1..]); - } - command.arg(json); - - // Fire-and-forget – we do not wait for completion. - if let Err(e) = command.spawn() { - warn!("failed to spawn notifier '{}': {e}", notify_command[0]); - } - } - - pub(crate) fn new(notify: Option>) -> Self { - Self { - notify_command: notify, - } - } -} - -/// User can configure a program that will receive notifications. Each -/// notification is serialized as JSON and passed as an argument to the -/// program. -#[derive(Debug, Clone, PartialEq, Serialize)] -#[serde(tag = "type", rename_all = "kebab-case")] -pub(crate) enum UserNotification { - #[serde(rename_all = "kebab-case")] - AgentTurnComplete { - thread_id: String, - turn_id: String, - cwd: String, - - /// Messages that the user sent to the agent to initiate the turn. - input_messages: Vec, - - /// The last message sent by the assistant in the turn. - last_assistant_message: Option, - }, -} - -#[cfg(test)] -mod tests { - use super::*; - use anyhow::Result; - - #[test] - fn test_user_notification() -> Result<()> { - let notification = UserNotification::AgentTurnComplete { - thread_id: "b5f6c1c2-1111-2222-3333-444455556666".to_string(), - turn_id: "12345".to_string(), - cwd: "/Users/example/project".to_string(), - input_messages: vec!["Rename `foo` to `bar` and update the callsites.".to_string()], - last_assistant_message: Some( - "Rename complete and verified `cargo build` succeeds.".to_string(), - ), - }; - let serialized = serde_json::to_string(¬ification)?; - assert_eq!( - serialized, - r#"{"type":"agent-turn-complete","thread-id":"b5f6c1c2-1111-2222-3333-444455556666","turn-id":"12345","cwd":"/Users/example/project","input-messages":["Rename `foo` to `bar` and update the callsites."],"last-assistant-message":"Rename complete and verified `cargo build` succeeds."}"# - ); - Ok(()) - } -} diff --git a/codex-rs/core/src/util.rs b/codex-rs/core/src/util.rs index 1a538da558..59fecb0a9e 100644 --- a/codex-rs/core/src/util.rs +++ b/codex-rs/core/src/util.rs @@ -37,7 +37,7 @@ macro_rules! feedback_tags { }; } -pub(crate) fn backoff(attempt: u64) -> Duration { +pub fn backoff(attempt: u64) -> Duration { let exp = BACKOFF_FACTOR.powi(attempt.saturating_sub(1) as i32); let base = (INITIAL_DELAY_MS as f64 * exp) as u64; let jitter = rand::rng().random_range(0.9..1.1); diff --git a/codex-rs/core/templates/collaboration_mode/default.md b/codex-rs/core/templates/collaboration_mode/default.md index c8154d10d9..4efd963ba0 100644 --- a/codex-rs/core/templates/collaboration_mode/default.md +++ b/codex-rs/core/templates/collaboration_mode/default.md @@ -2,8 +2,10 @@ You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active. +Your active mode changes only when new developer instructions with a different `...` change it; user requests or tool descriptions do not change mode by themselves. Known mode names are {{KNOWN_MODE_NAMES}}. + ## request_user_input availability -The `request_user_input` tool is unavailable in Default mode. If you call it while in Default mode, it will return an error. +{{REQUEST_USER_INPUT_AVAILABILITY}} If a decision is necessary and cannot be discovered from local context, ask the user directly. However, in Default mode you should strongly prefer executing the user's request rather than stopping to ask questions. diff --git a/codex-rs/core/tests/common/lib.rs b/codex-rs/core/tests/common/lib.rs index 5df2a74edd..3feadb0722 100644 --- a/codex-rs/core/tests/common/lib.rs +++ b/codex-rs/core/tests/common/lib.rs @@ -1,7 +1,6 @@ #![expect(clippy::expect_used)] use codex_utils_cargo_bin::CargoBinError; -use codex_utils_cargo_bin::find_resource; use tempfile::TempDir; use codex_core::CodexThread; @@ -147,40 +146,6 @@ pub fn load_sse_fixture_with_id_from_str(raw: &str, id: &str) -> String { .collect() } -/// Same as [`load_sse_fixture`], but replaces the placeholder `__ID__` in the -/// fixture template with the supplied identifier before parsing. This lets a -/// single JSON template be reused by multiple tests that each need a unique -/// `response_id`. -pub fn load_sse_fixture_with_id(path: impl AsRef, id: &str) -> String { - let p = path.as_ref(); - let full_path = match find_resource!(p) { - Ok(p) => p, - Err(err) => panic!( - "failed to find fixture template at {:?}: {err}", - path.as_ref() - ), - }; - - let raw = std::fs::read_to_string(full_path).expect("read fixture template"); - let replaced = raw.replace("__ID__", id); - let events: Vec = - serde_json::from_str(&replaced).expect("parse JSON fixture"); - events - .into_iter() - .map(|e| { - let kind = e - .get("type") - .and_then(|v| v.as_str()) - .expect("fixture event missing type"); - if e.as_object().map(|o| o.len() == 1).unwrap_or(false) { - format!("event: {kind}\n\n") - } else { - format!("event: {kind}\ndata: {e}\n\n") - } - }) - .collect() -} - pub async fn wait_for_event(codex: &CodexThread, predicate: F) -> codex_core::protocol::EventMsg where F: FnMut(&codex_core::protocol::EventMsg) -> bool, diff --git a/codex-rs/core/tests/common/responses.rs b/codex-rs/core/tests/common/responses.rs index 86a332874e..7d3b077085 100644 --- a/codex-rs/core/tests/common/responses.rs +++ b/codex-rs/core/tests/common/responses.rs @@ -375,6 +375,10 @@ pub fn sse(events: Vec) -> String { out } +pub fn sse_completed(id: &str) -> String { + sse(vec![ev_response_created(id), ev_completed(id)]) +} + /// Convenience: SSE event for a completed response with a specific id. pub fn ev_completed(id: &str) -> Value { serde_json::json!({ diff --git a/codex-rs/core/tests/fixtures/completed_template.json b/codex-rs/core/tests/fixtures/completed_template.json deleted file mode 100644 index 1774dc5e84..0000000000 --- a/codex-rs/core/tests/fixtures/completed_template.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "type": "response.completed", - "response": { - "id": "__ID__", - "usage": { - "input_tokens": 0, - "input_tokens_details": null, - "output_tokens": 0, - "output_tokens_details": null, - "total_tokens": 0 - }, - "output": [] - } - } -] diff --git a/codex-rs/core/tests/responses_headers.rs b/codex-rs/core/tests/responses_headers.rs index 86e511c472..83f1d10423 100644 --- a/codex-rs/core/tests/responses_headers.rs +++ b/codex-rs/core/tests/responses_headers.rs @@ -1,7 +1,6 @@ use std::process::Command; use std::sync::Arc; -use codex_app_server_protocol::AuthMode; use codex_core::AuthManager; use codex_core::CodexAuth; use codex_core::ContentItem; @@ -10,14 +9,12 @@ use codex_core::ModelProviderInfo; use codex_core::Prompt; use codex_core::ResponseEvent; use codex_core::ResponseItem; -use codex_core::TransportManager; -use codex_core::WEB_SEARCH_ELIGIBLE_HEADER; use codex_core::WireApi; use codex_core::models_manager::manager::ModelsManager; use codex_otel::OtelManager; +use codex_otel::TelemetryAuthMode; use codex_protocol::ThreadId; use codex_protocol::config_types::ReasoningSummary; -use codex_protocol::config_types::WebSearchMode; use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::SubAgentSource; use core_test_support::load_default_config_for_test; @@ -73,7 +70,7 @@ async fn responses_stream_includes_subagent_header_on_review() { let config = Arc::new(config); let conversation_id = ThreadId::new(); - let auth_mode = AuthMode::Chatgpt; + let auth_mode = TelemetryAuthMode::Chatgpt; let session_source = SessionSource::SubAgent(SubAgentSource::Review); let model_info = ModelsManager::construct_model_info_offline(model.as_str(), &config); let otel_manager = OtelManager::new( @@ -88,19 +85,18 @@ async fn responses_stream_includes_subagent_header_on_review() { session_source.clone(), ); - let mut client_session = ModelClient::new( - Arc::clone(&config), + let client = ModelClient::new( None, - model_info, - otel_manager, - provider, - effort, - summary, conversation_id, + provider.clone(), session_source, - TransportManager::new(), - ) - .new_session(None); + config.model_verbosity, + false, + false, + false, + None, + ); + let mut client_session = client.new_session(); let mut prompt = Prompt::default(); prompt.input = vec![ResponseItem::Message { @@ -113,7 +109,10 @@ async fn responses_stream_includes_subagent_header_on_review() { phase: None, }]; - let mut stream = client_session.stream(&prompt).await.expect("stream failed"); + let mut stream = client_session + .stream(&prompt, &model_info, &otel_manager, effort, summary, None) + .await + .expect("stream failed"); while let Some(event) = stream.next().await { if matches!(event, Ok(ResponseEvent::Completed { .. })) { break; @@ -172,7 +171,7 @@ async fn responses_stream_includes_subagent_header_on_other() { let config = Arc::new(config); let conversation_id = ThreadId::new(); - let auth_mode = AuthMode::Chatgpt; + let auth_mode = TelemetryAuthMode::Chatgpt; let session_source = SessionSource::SubAgent(SubAgentSource::Other("my-task".to_string())); let model_info = ModelsManager::construct_model_info_offline(model.as_str(), &config); @@ -188,19 +187,18 @@ async fn responses_stream_includes_subagent_header_on_other() { session_source.clone(), ); - let mut client_session = ModelClient::new( - Arc::clone(&config), + let client = ModelClient::new( None, - model_info, - otel_manager, - provider, - effort, - summary, conversation_id, + provider.clone(), session_source, - TransportManager::new(), - ) - .new_session(None); + config.model_verbosity, + false, + false, + false, + None, + ); + let mut client_session = client.new_session(); let mut prompt = Prompt::default(); prompt.input = vec![ResponseItem::Message { @@ -213,7 +211,10 @@ async fn responses_stream_includes_subagent_header_on_other() { phase: None, }]; - let mut stream = client_session.stream(&prompt).await.expect("stream failed"); + let mut stream = client_session + .stream(&prompt, &model_info, &otel_manager, effort, summary, None) + .await + .expect("stream failed"); while let Some(event) = stream.next().await { if matches!(event, Ok(ResponseEvent::Completed { .. })) { break; @@ -227,66 +228,6 @@ async fn responses_stream_includes_subagent_header_on_other() { ); } -#[tokio::test] -async fn responses_stream_includes_web_search_eligible_header_true_by_default() { - core_test_support::skip_if_no_network!(); - - let server = responses::start_mock_server().await; - let response_body = responses::sse(vec![ - responses::ev_response_created("resp-1"), - responses::ev_completed("resp-1"), - ]); - - let request_recorder = responses::mount_sse_once_match( - &server, - header(WEB_SEARCH_ELIGIBLE_HEADER, "true"), - response_body, - ) - .await; - - let test = test_codex().build(&server).await.expect("build test codex"); - test.submit_turn("hello").await.expect("submit test prompt"); - - let request = request_recorder.single_request(); - assert_eq!( - request.header(WEB_SEARCH_ELIGIBLE_HEADER).as_deref(), - Some("true") - ); -} - -#[tokio::test] -async fn responses_stream_includes_web_search_eligible_header_false_when_disabled() { - core_test_support::skip_if_no_network!(); - - let server = responses::start_mock_server().await; - let response_body = responses::sse(vec![ - responses::ev_response_created("resp-1"), - responses::ev_completed("resp-1"), - ]); - - let request_recorder = responses::mount_sse_once_match( - &server, - header(WEB_SEARCH_ELIGIBLE_HEADER, "false"), - response_body, - ) - .await; - - let test = test_codex() - .with_config(|config| { - config.web_search_mode = Some(WebSearchMode::Disabled); - }) - .build(&server) - .await - .expect("build test codex"); - test.submit_turn("hello").await.expect("submit test prompt"); - - let request = request_recorder.single_request(); - assert_eq!( - request.header(WEB_SEARCH_ELIGIBLE_HEADER).as_deref(), - Some("false") - ); -} - #[tokio::test] async fn responses_respects_model_info_overrides_from_config() { core_test_support::skip_if_no_network!(); @@ -329,8 +270,9 @@ async fn responses_respects_model_info_overrides_from_config() { let config = Arc::new(config); let conversation_id = ThreadId::new(); - let auth_mode = - AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key")).get_auth_mode(); + let auth_mode = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key")) + .auth_mode() + .map(TelemetryAuthMode::from); let session_source = SessionSource::SubAgent(SubAgentSource::Other("override-check".to_string())); let model_info = ModelsManager::construct_model_info_offline(model.as_str(), &config); @@ -346,19 +288,18 @@ async fn responses_respects_model_info_overrides_from_config() { session_source.clone(), ); - let mut client = ModelClient::new( - Arc::clone(&config), + let client = ModelClient::new( None, - model_info, - otel_manager, - provider, - effort, - summary, conversation_id, + provider.clone(), session_source, - TransportManager::new(), - ) - .new_session(None); + config.model_verbosity, + false, + false, + false, + None, + ); + let mut client_session = client.new_session(); let mut prompt = Prompt::default(); prompt.input = vec![ResponseItem::Message { @@ -371,7 +312,10 @@ async fn responses_respects_model_info_overrides_from_config() { phase: None, }]; - let mut stream = client.stream(&prompt).await.expect("stream failed"); + let mut stream = client_session + .stream(&prompt, &model_info, &otel_manager, effort, summary, None) + .await + .expect("stream failed"); while let Some(event) = stream.next().await { if matches!(event, Ok(ResponseEvent::Completed { .. })) { break; @@ -408,88 +352,14 @@ async fn responses_stream_includes_turn_metadata_header_for_git_workspace_e2e() responses::ev_response_created("resp-1"), responses::ev_completed("resp-1"), ]); - let provider = ModelProviderInfo { - name: "mock".into(), - base_url: Some(format!("{}/v1", server.uri())), - env_key: None, - env_key_instructions: None, - experimental_bearer_token: None, - wire_api: WireApi::Responses, - query_params: None, - http_headers: None, - env_http_headers: None, - request_max_retries: Some(0), - stream_max_retries: Some(0), - stream_idle_timeout_ms: Some(5_000), - requires_openai_auth: false, - supports_websockets: false, - }; - let codex_home = TempDir::new().expect("failed to create TempDir"); - let mut config = load_default_config_for_test(&codex_home).await; - config.model_provider_id = provider.name.clone(); - config.model_provider = provider.clone(); - let effort = config.model_reasoning_effort; - let summary = config.model_reasoning_summary; - let model = ModelsManager::get_model_offline(config.model.as_deref()); - config.model = Some(model.clone()); - let config = Arc::new(config); - - let conversation_id = ThreadId::new(); - let auth_mode = AuthMode::Chatgpt; - let session_source = - SessionSource::SubAgent(SubAgentSource::Other("turn-metadata-e2e".to_string())); - let model_info = ModelsManager::construct_model_info_offline(model.as_str(), &config); - let otel_manager = OtelManager::new( - conversation_id, - model.as_str(), - model_info.slug.as_str(), - None, - Some("test@test.com".to_string()), - Some(auth_mode), - false, - "test".to_string(), - session_source.clone(), - ); - - let client = ModelClient::new( - Arc::clone(&config), - None, - model_info, - otel_manager, - provider, - effort, - summary, - conversation_id, - session_source, - TransportManager::new(), - ); - - let workspace = TempDir::new().expect("workspace tempdir"); - let cwd = workspace.path(); - - let mut prompt = Prompt::default(); - prompt.input = vec![ResponseItem::Message { - id: None, - role: "user".into(), - content: vec![ContentItem::InputText { - text: "hello".into(), - }], - end_turn: None, - phase: None, - }]; + let test = test_codex().build(&server).await.expect("build test codex"); + let cwd = test.cwd_path(); let first_request = responses::mount_sse_once(&server, response_body.clone()).await; - let mut first_session = client.new_session(Some(cwd.to_path_buf())); - let mut first_stream = first_session - .stream(&prompt) + test.submit_turn("hello") .await - .expect("stream first turn"); - while let Some(event) = first_stream.next().await { - if matches!(event, Ok(ResponseEvent::Completed { .. })) { - break; - } - } + .expect("submit first turn prompt"); assert_eq!( first_request .single_request() @@ -539,21 +409,13 @@ async fn responses_stream_includes_turn_metadata_header_for_git_workspace_e2e() .trim() .to_string(); - let repo_root = std::fs::canonicalize(cwd) - .unwrap_or_else(|_| cwd.to_path_buf()) - .to_string_lossy() - .into_owned(); let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(5); loop { let request_recorder = responses::mount_sse_once(&server, response_body.clone()).await; - let mut session = client.new_session(Some(cwd.to_path_buf())); tokio::time::sleep(std::time::Duration::from_millis(50)).await; - let mut stream = session.stream(&prompt).await.expect("stream post-git turn"); - while let Some(event) = stream.next().await { - if matches!(event, Ok(ResponseEvent::Completed { .. })) { - break; - } - } + test.submit_turn("hello") + .await + .expect("submit post-git turn prompt"); let maybe_header = request_recorder .single_request() @@ -561,11 +423,14 @@ async fn responses_stream_includes_turn_metadata_header_for_git_workspace_e2e() if let Some(header_value) = maybe_header { let parsed: serde_json::Value = serde_json::from_str(&header_value) .expect("x-codex-turn-metadata should be valid JSON"); - let workspace = parsed + let workspaces = parsed .get("workspaces") .and_then(serde_json::Value::as_object) - .and_then(|workspaces| workspaces.get(&repo_root)) - .expect("metadata should include cwd repo root workspace entry"); + .expect("metadata should include workspaces"); + let workspace = workspaces + .values() + .next() + .expect("metadata should include at least one workspace entry"); assert_eq!( workspace diff --git a/codex-rs/core/tests/suite/cli_stream.rs b/codex-rs/core/tests/suite/cli_stream.rs index 291f596e27..106e2ff148 100644 --- a/codex-rs/core/tests/suite/cli_stream.rs +++ b/codex-rs/core/tests/suite/cli_stream.rs @@ -85,13 +85,8 @@ async fn responses_mode_stream_cli() { !page.items.is_empty(), "expected at least one session to be listed" ); - // First line of head must be the SessionMeta payload (id/timestamp) - let head0 = page.items[0].head.first().expect("missing head record"); - assert!(head0.get("id").is_some(), "head[0] missing id"); - assert!( - head0.get("timestamp").is_some(), - "head[0] missing timestamp" - ); + assert!(page.items[0].thread_id.is_some(), "missing thread_id"); + assert!(page.items[0].created_at.is_some(), "missing created_at"); } /// Verify that passing `-c model_instructions_file=...` to the CLI diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index bd50708a2c..fafa8b0865 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -11,7 +11,6 @@ use codex_core::Prompt; use codex_core::ResponseEvent; use codex_core::ResponseItem; use codex_core::ThreadManager; -use codex_core::TransportManager; use codex_core::WireApi; use codex_core::auth::AuthCredentialsStoreMode; use codex_core::built_in_model_providers; @@ -22,6 +21,7 @@ use codex_core::protocol::EventMsg; use codex_core::protocol::Op; use codex_core::protocol::SessionSource; use codex_otel::OtelManager; +use codex_otel::TelemetryAuthMode; use codex_protocol::ThreadId; use codex_protocol::config_types::CollaborationMode; use codex_protocol::config_types::ModeKind; @@ -36,8 +36,9 @@ use codex_protocol::models::WebSearchAction; use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::user_input::UserInput; use core_test_support::load_default_config_for_test; -use core_test_support::load_sse_fixture_with_id; +use core_test_support::responses::ev_completed; use core_test_support::responses::ev_completed_with_tokens; +use core_test_support::responses::ev_response_created; use core_test_support::responses::mount_sse_once; use core_test_support::responses::mount_sse_once_match; use core_test_support::responses::mount_sse_sequence; @@ -64,11 +65,6 @@ use wiremock::matchers::method; use wiremock::matchers::path; use wiremock::matchers::query_param; -/// Build minimal SSE stream with completed marker using the JSON fixture. -fn sse_completed(id: &str) -> String { - load_sse_fixture_with_id("../fixtures/completed_template.json", id) -} - #[expect(clippy::unwrap_used)] fn assert_message_role(request_body: &serde_json::Value, role: &str) { assert_eq!(request_body["role"].as_str().unwrap(), role); @@ -259,7 +255,11 @@ async fn resume_includes_initial_messages_and_sends_prior_items() { // Mock server that will receive the resumed request let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; // Configure Codex to resume from our file let codex_home = Arc::new(TempDir::new().unwrap()); @@ -377,7 +377,11 @@ async fn includes_conversation_id_and_model_headers_in_request() { // Mock server let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let mut builder = test_codex().with_auth(CodexAuth::from_api_key("Test API Key")); let test = builder @@ -418,7 +422,11 @@ async fn includes_base_instructions_override_in_request() { skip_if_no_network!(); // Mock server let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let mut builder = test_codex() .with_auth(CodexAuth::from_api_key("Test API Key")) @@ -462,7 +470,11 @@ async fn chatgpt_auth_sends_correct_request() { // Mock server let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let mut model_provider = built_in_model_providers()["openai"].clone(); model_provider.base_url = Some(format!("{}/api/codex", server.uri())); @@ -524,7 +536,10 @@ async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() { let first = ResponseTemplate::new(200) .insert_header("content-type", "text/event-stream") - .set_body_raw(sse_completed("resp1"), "text/event-stream"); + .set_body_raw( + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + "text/event-stream", + ); // Expect API key header, no ChatGPT account header required. Mock::given(method("POST")) @@ -590,7 +605,11 @@ async fn includes_user_instructions_message_in_request() { skip_if_no_network!(); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let mut builder = test_codex() .with_auth(CodexAuth::from_api_key("Test API Key")) @@ -652,7 +671,11 @@ async fn skills_append_to_instructions() { skip_if_no_network!(); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let codex_home = Arc::new(TempDir::new().unwrap()); let skill_dir = codex_home.path().join("skills/demo"); @@ -720,7 +743,11 @@ async fn includes_configured_effort_in_request() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let TestCodex { codex, .. } = test_codex() .with_model("gpt-5.1-codex") .with_config(|config| { @@ -761,7 +788,11 @@ async fn includes_no_effort_in_request() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let TestCodex { codex, .. } = test_codex() .with_model("gpt-5.1-codex") .build(&server) @@ -800,7 +831,11 @@ async fn includes_default_reasoning_effort_in_request_when_defined_by_model_info skip_if_no_network!(Ok(())); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let TestCodex { codex, .. } = test_codex().with_model("gpt-5.1").build(&server).await?; codex @@ -835,7 +870,11 @@ async fn user_turn_collaboration_mode_overrides_model_and_effort() -> anyhow::Re skip_if_no_network!(Ok(())); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let TestCodex { codex, config, @@ -893,7 +932,11 @@ async fn configured_reasoning_summary_is_sent() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let TestCodex { codex, .. } = test_codex() .with_config(|config| { config.model_reasoning_summary = ReasoningSummary::Concise; @@ -933,7 +976,11 @@ async fn reasoning_summary_is_omitted_when_disabled() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let TestCodex { codex, .. } = test_codex() .with_config(|config| { config.model_reasoning_summary = ReasoningSummary::None; @@ -972,7 +1019,11 @@ async fn includes_default_verbosity_in_request() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let TestCodex { codex, .. } = test_codex().with_model("gpt-5.1").build(&server).await?; codex @@ -1007,7 +1058,11 @@ async fn configured_verbosity_not_sent_for_models_without_support() -> anyhow::R skip_if_no_network!(Ok(())); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let TestCodex { codex, .. } = test_codex() .with_model("gpt-5.1-codex") .with_config(|config| { @@ -1047,7 +1102,11 @@ async fn configured_verbosity_is_sent() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let TestCodex { codex, .. } = test_codex() .with_model("gpt-5.1") .with_config(|config| { @@ -1088,7 +1147,11 @@ async fn includes_developer_instructions_message_in_request() { skip_if_no_network!(); let server = MockServer::start().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let mut builder = test_codex() .with_auth(CodexAuth::from_api_key("Test API Key")) .with_config(|config| { @@ -1195,25 +1258,24 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() { model_info.slug.as_str(), None, Some("test@test.com".to_string()), - auth_manager.get_auth_mode(), + auth_manager.auth_mode().map(TelemetryAuthMode::from), false, "test".to_string(), SessionSource::Exec, ); - let mut client = ModelClient::new( - Arc::clone(&config), + let client = ModelClient::new( None, - model_info, - otel_manager, - provider, - effort, - summary, conversation_id, + provider.clone(), SessionSource::Exec, - TransportManager::new(), - ) - .new_session(None); + config.model_verbosity, + false, + false, + false, + None, + ); + let mut client_session = client.new_session(); let mut prompt = Prompt::default(); prompt.input.push(ResponseItem::Reasoning { @@ -1251,10 +1313,7 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() { }); prompt.input.push(ResponseItem::FunctionCallOutput { call_id: "function-call-id".into(), - output: FunctionCallOutputPayload { - content: "ok".into(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("ok".into()), }); prompt.input.push(ResponseItem::LocalShellCall { id: Some("local-shell-id".into()), @@ -1280,8 +1339,8 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() { output: "ok".into(), }); - let mut stream = client - .stream(&prompt) + let mut stream = client_session + .stream(&prompt, &model_info, &otel_manager, effort, summary, None) .await .expect("responses stream to start"); @@ -1570,7 +1629,10 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res mount_sse_once_match( &server, body_string_contains("seed turn"), - sse_completed("resp_seed"), + sse(vec![ + ev_response_created("resp_seed"), + ev_completed("resp_seed"), + ]), ) .await; @@ -1656,7 +1718,10 @@ async fn azure_overrides_assign_properties_used_for_responses_url() { // First request – must NOT include `previous_response_id`. let first = ResponseTemplate::new(200) .insert_header("content-type", "text/event-stream") - .set_body_raw(sse_completed("resp1"), "text/event-stream"); + .set_body_raw( + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + "text/event-stream", + ); // Expect POST to /openai/responses with api-version query param Mock::given(method("POST")) @@ -1737,7 +1802,10 @@ async fn env_var_overrides_loaded_auth() { // First request – must NOT include `previous_response_id`. let first = ResponseTemplate::new(200) .insert_header("content-type", "text/event-stream") - .set_body_raw(sse_completed("resp1"), "text/event-stream"); + .set_body_raw( + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + "text/event-stream", + ); // Expect POST to /openai/responses with api-version query param Mock::given(method("POST")) diff --git a/codex-rs/core/tests/suite/client_websockets.rs b/codex-rs/core/tests/suite/client_websockets.rs index 51c94edd7b..bf170bb757 100644 --- a/codex-rs/core/tests/suite/client_websockets.rs +++ b/codex-rs/core/tests/suite/client_websockets.rs @@ -8,16 +8,20 @@ use codex_core::ModelProviderInfo; use codex_core::Prompt; use codex_core::ResponseEvent; use codex_core::ResponseItem; -use codex_core::TransportManager; use codex_core::WireApi; +use codex_core::X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER; use codex_core::features::Feature; use codex_core::models_manager::manager::ModelsManager; use codex_core::protocol::SessionSource; use codex_otel::OtelManager; +use codex_otel::TelemetryAuthMode; use codex_otel::metrics::MetricsClient; use codex_otel::metrics::MetricsConfig; use codex_protocol::ThreadId; +use codex_protocol::account::PlanType; use codex_protocol::config_types::ReasoningSummary; +use codex_protocol::openai_models::ModelInfo; +use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; use core_test_support::load_default_config_for_test; use core_test_support::responses::WebSocketConnectionConfig; use core_test_support::responses::WebSocketTestServer; @@ -29,16 +33,22 @@ use core_test_support::skip_if_no_network; use futures::StreamExt; use opentelemetry_sdk::metrics::InMemoryMetricExporter; use pretty_assertions::assert_eq; +use serde_json::json; use std::sync::Arc; use std::time::Duration; use tempfile::TempDir; use tracing_test::traced_test; const MODEL: &str = "gpt-5.2-codex"; +const OPENAI_BETA_HEADER: &str = "OpenAI-Beta"; +const OPENAI_BETA_RESPONSES_WEBSOCKETS: &str = "responses_websockets=2026-02-04"; struct WebsocketTestHarness { _codex_home: TempDir, client: ModelClient, + model_info: ModelInfo, + effort: Option, + summary: ReasoningSummary, otel_manager: OtelManager, } @@ -53,10 +63,10 @@ async fn responses_websocket_streams_request() { .await; let harness = websocket_harness(&server).await; - let mut session = harness.client.new_session(None); + let mut client_session = harness.client.new_session(); let prompt = prompt_with_input(vec![message_item("hello")]); - stream_until_complete(&mut session, &prompt).await; + stream_until_complete(&mut client_session, &harness, &prompt).await; let connection = server.single_connection(); assert_eq!(connection.len(), 1); @@ -66,6 +76,11 @@ async fn responses_websocket_streams_request() { assert_eq!(body["model"].as_str(), Some(MODEL)); assert_eq!(body["stream"], serde_json::Value::Bool(true)); assert_eq!(body["input"].as_array().map(Vec::len), Some(1)); + let handshake = server.single_handshake(); + assert_eq!( + handshake.header(OPENAI_BETA_HEADER), + Some(OPENAI_BETA_RESPONSES_WEBSOCKETS.to_string()) + ); server.shutdown().await; } @@ -83,10 +98,10 @@ async fn responses_websocket_emits_websocket_telemetry_events() { let harness = websocket_harness(&server).await; harness.otel_manager.reset_runtime_metrics(); - let mut session = harness.client.new_session(None); + let mut client_session = harness.client.new_session(); let prompt = prompt_with_input(vec![message_item("hello")]); - stream_until_complete(&mut session, &prompt).await; + stream_until_complete(&mut client_session, &harness, &prompt).await; tokio::time::sleep(Duration::from_millis(10)).await; @@ -102,6 +117,80 @@ async fn responses_websocket_emits_websocket_telemetry_events() { server.shutdown().await; } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn responses_websocket_includes_timing_metrics_header_when_runtime_metrics_enabled() { + skip_if_no_network!(); + + let server = start_websocket_server(vec![vec![vec![ + ev_response_created("resp-1"), + serde_json::json!({ + "type": "responsesapi.websocket_timing", + "timing_metrics": { + "responses_duration_excl_engine_and_client_tool_time_ms": 120, + "engine_service_total_ms": 450, + "engine_iapi_ttft_total_ms": 310, + "engine_service_ttft_total_ms": 340, + "engine_iapi_tbt_across_engine_calls_ms": 220, + "engine_service_tbt_across_engine_calls_ms": 260 + } + }), + ev_completed("resp-1"), + ]]]) + .await; + + let harness = websocket_harness_with_runtime_metrics(&server, true).await; + harness.otel_manager.reset_runtime_metrics(); + let mut client_session = harness.client.new_session(); + let prompt = prompt_with_input(vec![message_item("hello")]); + + stream_until_complete(&mut client_session, &harness, &prompt).await; + tokio::time::sleep(Duration::from_millis(10)).await; + + let handshake = server.single_handshake(); + assert_eq!( + handshake.header(X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER), + Some("true".to_string()) + ); + + let summary = harness + .otel_manager + .runtime_metrics_summary() + .expect("runtime metrics summary"); + assert_eq!(summary.responses_api_overhead_ms, 120); + assert_eq!(summary.responses_api_inference_time_ms, 450); + assert_eq!(summary.responses_api_engine_iapi_ttft_ms, 310); + assert_eq!(summary.responses_api_engine_service_ttft_ms, 340); + assert_eq!(summary.responses_api_engine_iapi_tbt_ms, 220); + assert_eq!(summary.responses_api_engine_service_tbt_ms, 260); + + server.shutdown().await; +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn responses_websocket_omits_timing_metrics_header_when_runtime_metrics_disabled() { + skip_if_no_network!(); + + let server = start_websocket_server(vec![vec![vec![ + ev_response_created("resp-1"), + ev_completed("resp-1"), + ]]]) + .await; + + let harness = websocket_harness_with_runtime_metrics(&server, false).await; + let mut client_session = harness.client.new_session(); + let prompt = prompt_with_input(vec![message_item("hello")]); + + stream_until_complete(&mut client_session, &harness, &prompt).await; + + let handshake = server.single_handshake(); + assert_eq!( + handshake.header(X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER), + None + ); + + server.shutdown().await; +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn responses_websocket_emits_reasoning_included_event() { skip_if_no_network!(); @@ -113,11 +202,18 @@ async fn responses_websocket_emits_reasoning_included_event() { .await; let harness = websocket_harness(&server).await; - let mut session = harness.client.new_session(None); + let mut client_session = harness.client.new_session(); let prompt = prompt_with_input(vec![message_item("hello")]); - let mut stream = session - .stream(&prompt) + let mut stream = client_session + .stream( + &prompt, + &harness.model_info, + &harness.otel_manager, + harness.effort, + harness.summary, + None, + ) .await .expect("websocket stream failed"); @@ -136,6 +232,97 @@ async fn responses_websocket_emits_reasoning_included_event() { server.shutdown().await; } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn responses_websocket_emits_rate_limit_events() { + skip_if_no_network!(); + + let rate_limit_event = json!({ + "type": "codex.rate_limits", + "plan_type": "plus", + "rate_limits": { + "allowed": true, + "limit_reached": false, + "primary": { + "used_percent": 42, + "window_minutes": 60, + "reset_at": 1700000000 + }, + "secondary": null + }, + "code_review_rate_limits": null, + "credits": { + "has_credits": true, + "unlimited": false, + "balance": "123" + }, + "promo": null + }); + + let server = start_websocket_server_with_headers(vec![WebSocketConnectionConfig { + requests: vec![vec![ + rate_limit_event, + ev_response_created("resp-1"), + ev_completed("resp-1"), + ]], + response_headers: vec![ + ("X-Models-Etag".to_string(), "etag-123".to_string()), + ("X-Reasoning-Included".to_string(), "true".to_string()), + ], + }]) + .await; + + let harness = websocket_harness(&server).await; + let mut client_session = harness.client.new_session(); + let prompt = prompt_with_input(vec![message_item("hello")]); + + let mut stream = client_session + .stream( + &prompt, + &harness.model_info, + &harness.otel_manager, + harness.effort, + harness.summary, + None, + ) + .await + .expect("websocket stream failed"); + + let mut saw_rate_limits = None; + let mut saw_models_etag = None; + let mut saw_reasoning_included = false; + + while let Some(event) = stream.next().await { + match event.expect("event") { + ResponseEvent::RateLimits(snapshot) => { + saw_rate_limits = Some(snapshot); + } + ResponseEvent::ModelsEtag(etag) => { + saw_models_etag = Some(etag); + } + ResponseEvent::ServerReasoningIncluded(true) => { + saw_reasoning_included = true; + } + ResponseEvent::Completed { .. } => break, + _ => {} + } + } + + let rate_limits = saw_rate_limits.expect("missing rate limits"); + let primary = rate_limits.primary.expect("missing primary window"); + assert_eq!(primary.used_percent, 42.0); + assert_eq!(primary.window_minutes, Some(60)); + assert_eq!(primary.resets_at, Some(1_700_000_000)); + assert_eq!(rate_limits.plan_type, Some(PlanType::Plus)); + let credits = rate_limits.credits.expect("missing credits"); + assert!(credits.has_credits); + assert!(!credits.unlimited); + assert_eq!(credits.balance.as_deref(), Some("123")); + assert_eq!(saw_models_etag.as_deref(), Some("etag-123")); + assert!(saw_reasoning_included); + + server.shutdown().await; +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn responses_websocket_appends_on_prefix() { skip_if_no_network!(); @@ -147,12 +334,12 @@ async fn responses_websocket_appends_on_prefix() { .await; let harness = websocket_harness(&server).await; - let mut session = harness.client.new_session(None); + let mut client_session = harness.client.new_session(); let prompt_one = prompt_with_input(vec![message_item("hello")]); let prompt_two = prompt_with_input(vec![message_item("hello"), message_item("second")]); - stream_until_complete(&mut session, &prompt_one).await; - stream_until_complete(&mut session, &prompt_two).await; + stream_until_complete(&mut client_session, &harness, &prompt_one).await; + stream_until_complete(&mut client_session, &harness, &prompt_two).await; let connection = server.single_connection(); assert_eq!(connection.len(), 2); @@ -183,12 +370,12 @@ async fn responses_websocket_creates_on_non_prefix() { .await; let harness = websocket_harness(&server).await; - let mut session = harness.client.new_session(None); + let mut client_session = harness.client.new_session(); let prompt_one = prompt_with_input(vec![message_item("hello")]); let prompt_two = prompt_with_input(vec![message_item("different")]); - stream_until_complete(&mut session, &prompt_one).await; - stream_until_complete(&mut session, &prompt_two).await; + stream_until_complete(&mut client_session, &harness, &prompt_one).await; + stream_until_complete(&mut client_session, &harness, &prompt_two).await; let connection = server.single_connection(); assert_eq!(connection.len(), 2); @@ -241,11 +428,21 @@ fn websocket_provider(server: &WebSocketTestServer) -> ModelProviderInfo { } async fn websocket_harness(server: &WebSocketTestServer) -> WebsocketTestHarness { + websocket_harness_with_runtime_metrics(server, false).await +} + +async fn websocket_harness_with_runtime_metrics( + server: &WebSocketTestServer, + runtime_metrics_enabled: bool, +) -> WebsocketTestHarness { let provider = websocket_provider(server); let codex_home = TempDir::new().unwrap(); let mut config = load_default_config_for_test(&codex_home).await; config.model = Some(MODEL.to_string()); config.features.enable(Feature::ResponsesWebsockets); + if runtime_metrics_enabled { + config.features.enable(Feature::RuntimeMetrics); + } let config = Arc::new(config); let model_info = ModelsManager::construct_model_info_offline(MODEL, &config); let conversation_id = ThreadId::new(); @@ -262,35 +459,50 @@ async fn websocket_harness(server: &WebSocketTestServer) -> WebsocketTestHarness model_info.slug.as_str(), None, Some("test@test.com".to_string()), - auth_manager.get_auth_mode(), + auth_manager.auth_mode().map(TelemetryAuthMode::from), false, "test".to_string(), SessionSource::Exec, ) .with_metrics(metrics); + let effort = None; + let summary = ReasoningSummary::Auto; let client = ModelClient::new( - Arc::clone(&config), None, - model_info, - otel_manager.clone(), - provider.clone(), - None, - ReasoningSummary::Auto, conversation_id, + provider.clone(), SessionSource::Exec, - TransportManager::new(), + config.model_verbosity, + true, + false, + runtime_metrics_enabled, + None, ); WebsocketTestHarness { _codex_home: codex_home, client, + model_info, + effort, + summary, otel_manager, } } -async fn stream_until_complete(session: &mut ModelClientSession, prompt: &Prompt) { - let mut stream = session - .stream(prompt) +async fn stream_until_complete( + client_session: &mut ModelClientSession, + harness: &WebsocketTestHarness, + prompt: &Prompt, +) { + let mut stream = client_session + .stream( + prompt, + &harness.model_info, + &harness.otel_manager, + harness.effort, + harness.summary, + None, + ) .await .expect("websocket stream failed"); diff --git a/codex-rs/core/tests/suite/collaboration_instructions.rs b/codex-rs/core/tests/suite/collaboration_instructions.rs index 21a0b27613..160866819a 100644 --- a/codex-rs/core/tests/suite/collaboration_instructions.rs +++ b/codex-rs/core/tests/suite/collaboration_instructions.rs @@ -18,10 +18,6 @@ use core_test_support::wait_for_event; use pretty_assertions::assert_eq; use serde_json::Value; -fn sse_completed(id: &str) -> String { - sse(vec![ev_response_created(id), ev_completed(id)]) -} - fn collab_mode_with_mode_and_instructions( mode: ModeKind, instructions: Option<&str>, @@ -72,7 +68,11 @@ async fn no_collaboration_instructions_by_default() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req = mount_sse_once(&server, sse_completed("resp-1")).await; + let req = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; let test = test_codex().build(&server).await?; @@ -100,7 +100,11 @@ async fn user_input_includes_collaboration_instructions_after_override() -> Resu skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req = mount_sse_once(&server, sse_completed("resp-1")).await; + let req = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; let test = test_codex().build(&server).await?; @@ -144,7 +148,11 @@ async fn collaboration_instructions_added_on_user_turn() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req = mount_sse_once(&server, sse_completed("resp-1")).await; + let req = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; let test = test_codex().build(&server).await?; let collab_text = "turn instructions"; @@ -182,7 +190,11 @@ async fn override_then_next_turn_uses_updated_collaboration_instructions() -> Re skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req = mount_sse_once(&server, sse_completed("resp-1")).await; + let req = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; let test = test_codex().build(&server).await?; let collab_text = "override instructions"; @@ -226,7 +238,11 @@ async fn user_turn_overrides_collaboration_instructions_after_override() -> Resu skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req = mount_sse_once(&server, sse_completed("resp-1")).await; + let req = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; let test = test_codex().build(&server).await?; let base_text = "base instructions"; @@ -282,8 +298,16 @@ async fn collaboration_mode_update_emits_new_instruction_message() -> Result<()> skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let _req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let test = test_codex().build(&server).await?; let first_text = "first instructions"; @@ -354,8 +378,16 @@ async fn collaboration_mode_update_noop_does_not_append() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let _req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let test = test_codex().build(&server).await?; let collab_text = "same instructions"; @@ -423,8 +455,16 @@ async fn collaboration_mode_update_emits_new_instruction_message_when_mode_chang skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let _req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let test = test_codex().build(&server).await?; let default_text = "default mode instructions"; @@ -501,8 +541,16 @@ async fn collaboration_mode_update_noop_does_not_append_when_mode_is_unchanged() skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let _req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let test = test_codex().build(&server).await?; let collab_text = "mode-stable instructions"; @@ -576,8 +624,16 @@ async fn resume_replays_collaboration_instructions() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let _req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let mut builder = test_codex(); let initial = builder.build(&server).await?; @@ -642,9 +698,14 @@ async fn empty_collaboration_instructions_are_ignored() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req = mount_sse_once(&server, sse_completed("resp-1")).await; + let req = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; let test = test_codex().build(&server).await?; + let current_model = test.session_configured.model.clone(); test.codex .submit(Op::OverrideTurnContext { @@ -655,7 +716,14 @@ async fn empty_collaboration_instructions_are_ignored() -> Result<()> { model: None, effort: None, summary: None, - collaboration_mode: Some(collab_mode_with_instructions(Some(""))), + collaboration_mode: Some(CollaborationMode { + mode: ModeKind::Default, + settings: Settings { + model: current_model, + reasoning_effort: None, + developer_instructions: Some("".to_string()), + }, + }), personality: None, }) .await?; diff --git a/codex-rs/core/tests/suite/compact_remote.rs b/codex-rs/core/tests/suite/compact_remote.rs index b0aff9efaa..435aa8900c 100644 --- a/codex-rs/core/tests/suite/compact_remote.rs +++ b/codex-rs/core/tests/suite/compact_remote.rs @@ -25,6 +25,21 @@ use core_test_support::wait_for_event; use core_test_support::wait_for_event_match; use pretty_assertions::assert_eq; +fn approx_token_count(text: &str) -> i64 { + i64::try_from(text.len().saturating_add(3) / 4).unwrap_or(i64::MAX) +} + +fn estimate_compact_input_tokens(request: &responses::ResponsesRequest) -> i64 { + request.input().into_iter().fold(0i64, |acc, item| { + acc.saturating_add(approx_token_count(&item.to_string())) + }) +} + +fn estimate_compact_payload_tokens(request: &responses::ResponsesRequest) -> i64 { + estimate_compact_input_tokens(request) + .saturating_add(approx_token_count(&request.instructions_text())) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn remote_compact_replaces_history_for_followups() -> Result<()> { skip_if_no_network!(Ok(())); @@ -351,6 +366,210 @@ async fn remote_compact_trims_function_call_history_to_fit_context_window() -> R Ok(()) } +#[cfg_attr(target_os = "windows", ignore)] +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn remote_compact_trim_estimate_uses_session_base_instructions() -> Result<()> { + skip_if_no_network!(Ok(())); + + let first_user_message = "turn with baseline shell call"; + let second_user_message = "turn with trailing shell call"; + let baseline_retained_call_id = "baseline-retained-call"; + let baseline_trailing_call_id = "baseline-trailing-call"; + let override_retained_call_id = "override-retained-call"; + let override_trailing_call_id = "override-trailing-call"; + let retained_command = "printf retained-shell-output"; + let trailing_command = "printf trailing-shell-output"; + + let baseline_harness = TestCodexHarness::with_builder( + test_codex() + .with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing()) + .with_config(|config| { + config.features.enable(Feature::RemoteCompaction); + config.model_context_window = Some(200_000); + }), + ) + .await?; + let baseline_codex = baseline_harness.test().codex.clone(); + + responses::mount_sse_sequence( + baseline_harness.server(), + vec![ + sse(vec![ + responses::ev_shell_command_call(baseline_retained_call_id, retained_command), + responses::ev_completed("baseline-retained-call-response"), + ]), + sse(vec![ + responses::ev_assistant_message("baseline-retained-assistant", "retained complete"), + responses::ev_completed("baseline-retained-final-response"), + ]), + sse(vec![ + responses::ev_shell_command_call(baseline_trailing_call_id, trailing_command), + responses::ev_completed("baseline-trailing-call-response"), + ]), + sse(vec![responses::ev_completed( + "baseline-trailing-final-response", + )]), + ], + ) + .await; + + baseline_codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: first_user_message.into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + }) + .await?; + wait_for_event(&baseline_codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + baseline_codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: second_user_message.into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + }) + .await?; + wait_for_event(&baseline_codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + let baseline_compact_mock = responses::mount_compact_json_once( + baseline_harness.server(), + serde_json::json!({ "output": [] }), + ) + .await; + + baseline_codex.submit(Op::Compact).await?; + wait_for_event(&baseline_codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + let baseline_compact_request = baseline_compact_mock.single_request(); + assert!( + baseline_compact_request.has_function_call(baseline_retained_call_id), + "expected baseline compact request to retain older function call history" + ); + assert!( + baseline_compact_request.has_function_call(baseline_trailing_call_id), + "expected baseline compact request to retain trailing function call history" + ); + + let baseline_input_tokens = estimate_compact_input_tokens(&baseline_compact_request); + let baseline_payload_tokens = estimate_compact_payload_tokens(&baseline_compact_request); + + let override_base_instructions = + format!("REMOTE_BASE_INSTRUCTIONS_OVERRIDE {}", "x".repeat(120_000)); + let override_context_window = baseline_payload_tokens.saturating_add(1_000); + let pretrim_override_estimate = + baseline_input_tokens.saturating_add(approx_token_count(&override_base_instructions)); + assert!( + pretrim_override_estimate > override_context_window, + "expected override instructions to push pre-trim estimate past the context window" + ); + + let override_harness = TestCodexHarness::with_builder( + test_codex() + .with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing()) + .with_config({ + let override_base_instructions = override_base_instructions.clone(); + move |config| { + config.features.enable(Feature::RemoteCompaction); + config.model_context_window = Some(override_context_window); + config.base_instructions = Some(override_base_instructions); + } + }), + ) + .await?; + let override_codex = override_harness.test().codex.clone(); + + responses::mount_sse_sequence( + override_harness.server(), + vec![ + sse(vec![ + responses::ev_shell_command_call(override_retained_call_id, retained_command), + responses::ev_completed("override-retained-call-response"), + ]), + sse(vec![ + responses::ev_assistant_message("override-retained-assistant", "retained complete"), + responses::ev_completed("override-retained-final-response"), + ]), + sse(vec![ + responses::ev_shell_command_call(override_trailing_call_id, trailing_command), + responses::ev_completed("override-trailing-call-response"), + ]), + sse(vec![responses::ev_completed( + "override-trailing-final-response", + )]), + ], + ) + .await; + + override_codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: first_user_message.into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + }) + .await?; + wait_for_event(&override_codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + override_codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: second_user_message.into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + }) + .await?; + wait_for_event(&override_codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + let override_compact_mock = responses::mount_compact_json_once( + override_harness.server(), + serde_json::json!({ "output": [] }), + ) + .await; + + override_codex.submit(Op::Compact).await?; + wait_for_event(&override_codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + let override_compact_request = override_compact_mock.single_request(); + assert_eq!( + override_compact_request.instructions_text(), + override_base_instructions + ); + assert!( + override_compact_request.has_function_call(override_retained_call_id), + "expected remote compact request to preserve older function call history" + ); + assert!( + !override_compact_request.has_function_call(override_trailing_call_id), + "expected remote compact request to trim trailing function call history with override instructions" + ); + + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn remote_manual_compact_emits_context_compaction_items() -> Result<()> { skip_if_no_network!(Ok(())); diff --git a/codex-rs/core/tests/suite/exec.rs b/codex-rs/core/tests/suite/exec.rs index cdf597a4e9..b70062d7ed 100644 --- a/codex-rs/core/tests/suite/exec.rs +++ b/codex-rs/core/tests/suite/exec.rs @@ -44,7 +44,7 @@ async fn run_test_cmd(tmp: TempDir, cmd: Vec<&str>) -> Result String { - core_test_support::load_sse_fixture_with_id("../fixtures/completed_template.json", id) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn fork_thread_twice_drops_to_first_message() { skip_if_no_network!(); // Start a mock server that completes three turns. let server = MockServer::start().await; - let sse = sse_completed("resp"); + let sse = sse(vec![ev_response_created("resp"), ev_completed("resp")]); let first = ResponseTemplate::new(200) .insert_header("content-type", "text/event-stream") .set_body_raw(sse.clone(), "text/event-stream"); diff --git a/codex-rs/core/tests/suite/hierarchical_agents.rs b/codex-rs/core/tests/suite/hierarchical_agents.rs index 5dfa636489..5f1f84340d 100644 --- a/codex-rs/core/tests/suite/hierarchical_agents.rs +++ b/codex-rs/core/tests/suite/hierarchical_agents.rs @@ -1,20 +1,22 @@ use codex_core::features::Feature; -use core_test_support::load_sse_fixture_with_id; +use core_test_support::responses::ev_completed; +use core_test_support::responses::ev_response_created; use core_test_support::responses::mount_sse_once; +use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; use core_test_support::test_codex::test_codex; const HIERARCHICAL_AGENTS_SNIPPET: &str = "Files called AGENTS.md commonly appear in many places inside a container"; -fn sse_completed(id: &str) -> String { - load_sse_fixture_with_id("../fixtures/completed_template.json", id) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn hierarchical_agents_appends_to_project_doc_in_user_instructions() { let server = start_mock_server().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let mut builder = test_codex().with_config(|config| { config.features.enable(Feature::ChildAgentsMd); @@ -49,7 +51,11 @@ async fn hierarchical_agents_appends_to_project_doc_in_user_instructions() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn hierarchical_agents_emits_when_no_project_doc() { let server = start_mock_server().await; - let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await; + let resp_mock = mount_sse_once( + &server, + sse(vec![ev_response_created("resp1"), ev_completed("resp1")]), + ) + .await; let mut builder = test_codex().with_config(|config| { config.features.enable(Feature::ChildAgentsMd); diff --git a/codex-rs/core/tests/suite/live_reload.rs b/codex-rs/core/tests/suite/live_reload.rs new file mode 100644 index 0000000000..e46c77128f --- /dev/null +++ b/codex-rs/core/tests/suite/live_reload.rs @@ -0,0 +1,151 @@ +#![allow(clippy::expect_used, clippy::unwrap_used)] + +use std::fs; +use std::path::Path; +use std::path::PathBuf; +use std::time::Duration; + +use anyhow::Result; +use codex_core::config::ProjectConfig; +use codex_core::protocol::AskForApproval; +use codex_core::protocol::EventMsg; +use codex_core::protocol::Op; +use codex_core::protocol::SandboxPolicy; +use codex_protocol::config_types::ReasoningSummary; +use codex_protocol::config_types::TrustLevel; +use codex_protocol::user_input::UserInput; +use core_test_support::responses; +use core_test_support::responses::ResponsesRequest; +use core_test_support::responses::mount_sse_sequence; +use core_test_support::responses::start_mock_server; +use core_test_support::test_codex::TestCodex; +use core_test_support::test_codex::test_codex; +use core_test_support::wait_for_event; +use tokio::time::timeout; + +fn enable_trusted_project(config: &mut codex_core::config::Config) { + config.active_project = ProjectConfig { + trust_level: Some(TrustLevel::Trusted), + }; +} + +fn write_skill(home: &Path, name: &str, description: &str, body: &str) -> PathBuf { + let skill_dir = home.join("skills").join(name); + fs::create_dir_all(&skill_dir).expect("create skill dir"); + let contents = format!("---\nname: {name}\ndescription: {description}\n---\n\n{body}\n"); + let path = skill_dir.join("SKILL.md"); + fs::write(&path, contents).expect("write skill"); + path +} + +fn contains_skill_body(request: &ResponsesRequest, skill_body: &str) -> bool { + request + .message_input_texts("user") + .iter() + .any(|text| text.contains(skill_body) && text.contains("")) +} + +async fn submit_skill_turn(test: &TestCodex, skill_path: PathBuf, prompt: &str) -> Result<()> { + let session_model = test.session_configured.model.clone(); + test.codex + .submit(Op::UserTurn { + items: vec![ + UserInput::Text { + text: prompt.to_string(), + text_elements: Vec::new(), + }, + UserInput::Skill { + name: "demo".to_string(), + path: skill_path, + }, + ], + final_output_json_schema: None, + cwd: test.cwd_path().to_path_buf(), + approval_policy: AskForApproval::Never, + sandbox_policy: SandboxPolicy::DangerFullAccess, + model: session_model, + effort: None, + summary: ReasoningSummary::Auto, + collaboration_mode: None, + personality: None, + }) + .await?; + + wait_for_event(test.codex.as_ref(), |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn live_skills_reload_refreshes_skill_cache_after_skill_change() -> Result<()> { + let server = start_mock_server().await; + let responses = mount_sse_sequence( + &server, + vec![ + responses::sse(vec![responses::ev_completed("resp-1")]), + responses::sse(vec![responses::ev_completed("resp-2")]), + ], + ) + .await; + + let skill_v1 = "skill body v1"; + let skill_v2 = "skill body v2"; + let mut builder = test_codex() + .with_pre_build_hook(move |home| { + write_skill(home, "demo", "demo skill", skill_v1); + }) + .with_config(|config| { + enable_trusted_project(config); + }); + let test = builder.build(&server).await?; + + let skill_path = std::fs::canonicalize(test.codex_home_path().join("skills/demo/SKILL.md"))?; + + submit_skill_turn(&test, skill_path.clone(), "please use $demo").await?; + let first_request = responses + .requests() + .first() + .cloned() + .expect("first request captured"); + assert!( + contains_skill_body(&first_request, skill_v1), + "expected initial skill body in request" + ); + + write_skill(test.codex_home_path(), "demo", "demo skill", skill_v2); + + let saw_skills_update = timeout(Duration::from_secs(5), async { + loop { + match test.codex.next_event().await { + Ok(event) => { + if matches!(event.msg, EventMsg::SkillsUpdateAvailable) { + break; + } + } + Err(err) => panic!("event stream ended unexpectedly: {err}"), + } + } + }) + .await; + + if saw_skills_update.is_err() { + // Some environments do not reliably surface file watcher events for + // skill changes. Clear the cache explicitly so we can still validate + // that the updated skill body is injected on the next turn. + test.thread_manager.skills_manager().clear_cache(); + } + + submit_skill_turn(&test, skill_path.clone(), "please use $demo again").await?; + let last_request = responses + .last_request() + .expect("request captured after skill update"); + + assert!( + contains_skill_body(&last_request, skill_v2), + "expected updated skill body after reload" + ); + + Ok(()) +} diff --git a/codex-rs/core/tests/suite/memory_tool.rs b/codex-rs/core/tests/suite/memory_tool.rs new file mode 100644 index 0000000000..09d1ee3ced --- /dev/null +++ b/codex-rs/core/tests/suite/memory_tool.rs @@ -0,0 +1,84 @@ +#![allow(clippy::expect_used, clippy::unwrap_used)] + +use anyhow::Result; +use codex_core::features::Feature; +use core_test_support::responses::mount_function_call_agent_response; +use core_test_support::responses::start_mock_server; +use core_test_support::skip_if_no_network; +use core_test_support::test_codex::test_codex; +use pretty_assertions::assert_eq; +use serde_json::Value; +use serde_json::json; +use tokio::time::Duration; + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn get_memory_tool_returns_persisted_thread_memory() -> Result<()> { + skip_if_no_network!(Ok(())); + + let server = start_mock_server().await; + let mut builder = test_codex().with_config(|config| { + config.features.enable(Feature::Sqlite); + config.features.enable(Feature::MemoryTool); + }); + let test = builder.build(&server).await?; + + let db = test.codex.state_db().expect("state db enabled"); + let thread_id = test.session_configured.session_id; + let thread_id_string = thread_id.to_string(); + + let mut thread_exists = false; + // Wait for DB creation. + for _ in 0..100 { + if db.get_thread(thread_id).await?.is_some() { + thread_exists = true; + break; + } + tokio::time::sleep(Duration::from_millis(25)).await; + } + assert!(thread_exists, "thread should exist in state db"); + + let trace_summary = "trace summary from sqlite"; + let memory_summary = "memory summary from sqlite"; + db.upsert_thread_memory(thread_id, trace_summary, memory_summary) + .await?; + + let call_id = "memory-call-1"; + let arguments = json!({ + "memory_id": thread_id_string, + }) + .to_string(); + let mocks = + mount_function_call_agent_response(&server, call_id, &arguments, "get_memory").await; + + test.submit_turn("load the saved memory").await?; + + let initial_request = mocks.function_call.single_request().body_json(); + assert!( + initial_request["tools"] + .as_array() + .expect("tools array") + .iter() + .filter_map(|tool| tool.get("name").and_then(Value::as_str)) + .any(|name| name == "get_memory"), + "get_memory tool should be exposed when memory_tool feature is enabled" + ); + + let completion_request = mocks.completion.single_request(); + let (content_opt, success_opt) = completion_request + .function_call_output_content_and_success(call_id) + .expect("function_call_output should be present"); + let success = success_opt.unwrap_or(true); + assert!(success, "expected successful get_memory tool call output"); + let content = content_opt.expect("function_call_output content should be present"); + let payload: Value = serde_json::from_str(&content)?; + assert_eq!( + payload, + json!({ + "memory_id": thread_id_string, + "trace_summary": trace_summary, + "memory_summary": memory_summary, + }) + ); + + Ok(()) +} diff --git a/codex-rs/core/tests/suite/mod.rs b/codex-rs/core/tests/suite/mod.rs index 47a0829898..379f521682 100644 --- a/codex-rs/core/tests/suite/mod.rs +++ b/codex-rs/core/tests/suite/mod.rs @@ -81,8 +81,11 @@ mod json_result; mod list_dir; mod list_models; mod live_cli; +mod live_reload; +mod memory_tool; mod model_info_overrides; mod model_overrides; +mod model_switching; mod model_tools; mod models_cache_ttl; mod models_etag_responses; diff --git a/codex-rs/core/tests/suite/model_switching.rs b/codex-rs/core/tests/suite/model_switching.rs new file mode 100644 index 0000000000..73e957bdc4 --- /dev/null +++ b/codex-rs/core/tests/suite/model_switching.rs @@ -0,0 +1,192 @@ +use anyhow::Result; +use codex_core::config::types::Personality; +use codex_core::features::Feature; +use codex_core::protocol::AskForApproval; +use codex_core::protocol::EventMsg; +use codex_core::protocol::Op; +use codex_core::protocol::SandboxPolicy; +use codex_protocol::config_types::ReasoningSummary; +use codex_protocol::user_input::UserInput; +use core_test_support::responses::mount_sse_sequence; +use core_test_support::responses::sse_completed; +use core_test_support::responses::start_mock_server; +use core_test_support::skip_if_no_network; +use core_test_support::test_codex::test_codex; +use core_test_support::wait_for_event; +use pretty_assertions::assert_eq; + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn model_change_appends_model_instructions_developer_message() -> Result<()> { + skip_if_no_network!(Ok(())); + + let server = start_mock_server().await; + let resp_mock = mount_sse_sequence( + &server, + vec![sse_completed("resp-1"), sse_completed("resp-2")], + ) + .await; + + let mut builder = test_codex().with_model("gpt-5.2-codex"); + let test = builder.build(&server).await?; + let next_model = "gpt-5.1-codex-max"; + + test.codex + .submit(Op::UserTurn { + items: vec![UserInput::Text { + text: "hello".into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + cwd: test.cwd_path().to_path_buf(), + approval_policy: AskForApproval::Never, + sandbox_policy: SandboxPolicy::ReadOnly, + model: test.session_configured.model.clone(), + effort: test.config.model_reasoning_effort, + summary: ReasoningSummary::Auto, + collaboration_mode: None, + personality: None, + }) + .await?; + wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; + + test.codex + .submit(Op::OverrideTurnContext { + cwd: None, + approval_policy: None, + sandbox_policy: None, + windows_sandbox_level: None, + model: Some(next_model.to_string()), + effort: None, + summary: None, + collaboration_mode: None, + personality: None, + }) + .await?; + + test.codex + .submit(Op::UserTurn { + items: vec![UserInput::Text { + text: "switch models".into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + cwd: test.cwd_path().to_path_buf(), + approval_policy: AskForApproval::Never, + sandbox_policy: SandboxPolicy::ReadOnly, + model: next_model.to_string(), + effort: test.config.model_reasoning_effort, + summary: ReasoningSummary::Auto, + collaboration_mode: None, + personality: None, + }) + .await?; + wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; + + let requests = resp_mock.requests(); + assert_eq!(requests.len(), 2, "expected two model requests"); + + let second_request = requests.last().expect("expected second request"); + let developer_texts = second_request.message_input_texts("developer"); + let model_switch_text = developer_texts + .iter() + .find(|text| text.contains("")) + .expect("expected model switch message in developer input"); + assert!( + model_switch_text.contains("The user was previously using a different model."), + "expected model switch preamble, got: {model_switch_text:?}" + ); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn model_and_personality_change_only_appends_model_instructions() -> Result<()> { + skip_if_no_network!(Ok(())); + + let server = start_mock_server().await; + let resp_mock = mount_sse_sequence( + &server, + vec![sse_completed("resp-1"), sse_completed("resp-2")], + ) + .await; + + let mut builder = test_codex() + .with_model("gpt-5.2-codex") + .with_config(|config| { + config.features.enable(Feature::Personality); + }); + let test = builder.build(&server).await?; + let next_model = "exp-codex-personality"; + + test.codex + .submit(Op::UserTurn { + items: vec![UserInput::Text { + text: "hello".into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + cwd: test.cwd_path().to_path_buf(), + approval_policy: AskForApproval::Never, + sandbox_policy: SandboxPolicy::ReadOnly, + model: test.session_configured.model.clone(), + effort: test.config.model_reasoning_effort, + summary: ReasoningSummary::Auto, + collaboration_mode: None, + personality: None, + }) + .await?; + wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; + + test.codex + .submit(Op::OverrideTurnContext { + cwd: None, + approval_policy: None, + sandbox_policy: None, + windows_sandbox_level: None, + model: Some(next_model.to_string()), + effort: None, + summary: None, + collaboration_mode: None, + personality: Some(Personality::Pragmatic), + }) + .await?; + + test.codex + .submit(Op::UserTurn { + items: vec![UserInput::Text { + text: "switch model and personality".into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + cwd: test.cwd_path().to_path_buf(), + approval_policy: AskForApproval::Never, + sandbox_policy: SandboxPolicy::ReadOnly, + model: next_model.to_string(), + effort: test.config.model_reasoning_effort, + summary: ReasoningSummary::Auto, + collaboration_mode: None, + personality: None, + }) + .await?; + wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; + + let requests = resp_mock.requests(); + assert_eq!(requests.len(), 2, "expected two model requests"); + + let second_request = requests.last().expect("expected second request"); + let developer_texts = second_request.message_input_texts("developer"); + assert!( + developer_texts + .iter() + .any(|text| text.contains("")), + "expected model switch message when model changes" + ); + assert!( + !developer_texts + .iter() + .any(|text| text.contains("")), + "did not expect personality update message when model changed in same turn" + ); + + Ok(()) +} diff --git a/codex-rs/core/tests/suite/model_tools.rs b/codex-rs/core/tests/suite/model_tools.rs index b08b5281c8..d74ab0d7be 100644 --- a/codex-rs/core/tests/suite/model_tools.rs +++ b/codex-rs/core/tests/suite/model_tools.rs @@ -2,16 +2,11 @@ use codex_core::features::Feature; use codex_protocol::config_types::WebSearchMode; -use core_test_support::load_sse_fixture_with_id; use core_test_support::responses; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; use core_test_support::test_codex::test_codex; -fn sse_completed(id: &str) -> String { - load_sse_fixture_with_id("../fixtures/completed_template.json", id) -} - #[allow(clippy::expect_used)] fn tool_identifiers(body: &serde_json::Value) -> Vec { body["tools"] @@ -31,7 +26,10 @@ fn tool_identifiers(body: &serde_json::Value) -> Vec { #[allow(clippy::expect_used)] async fn collect_tool_identifiers_for_model(model: &str) -> Vec { let server = start_mock_server().await; - let sse = sse_completed(model); + let sse = responses::sse(vec![ + responses::ev_response_created(model), + responses::ev_completed(model), + ]); let resp_mock = responses::mount_sse_once(&server, sse).await; let mut builder = test_codex() @@ -52,6 +50,16 @@ async fn collect_tool_identifiers_for_model(model: &str) -> Vec { tool_identifiers(&body) } +fn expected_default_tools(shell_tool: &str, tail: &[&str]) -> Vec { + let mut tools = if cfg!(windows) { + vec![shell_tool.to_string()] + } else { + vec!["exec_command".to_string(), "write_stdin".to_string()] + }; + tools.extend(tail.iter().map(|tool| (*tool).to_string())); + tools +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn model_selects_expected_tools() { skip_if_no_network!(); @@ -60,83 +68,93 @@ async fn model_selects_expected_tools() { let codex_tools = collect_tool_identifiers_for_model("codex-mini-latest").await; assert_eq!( codex_tools, - vec![ - "local_shell".to_string(), - "list_mcp_resources".to_string(), - "list_mcp_resource_templates".to_string(), - "read_mcp_resource".to_string(), - "update_plan".to_string(), - "request_user_input".to_string(), - "web_search".to_string(), - "view_image".to_string() - ], + expected_default_tools( + "local_shell", + &[ + "list_mcp_resources", + "list_mcp_resource_templates", + "read_mcp_resource", + "update_plan", + "request_user_input", + "web_search", + "view_image", + ], + ), "codex-mini-latest should expose the local shell tool", ); let gpt5_codex_tools = collect_tool_identifiers_for_model("gpt-5-codex").await; assert_eq!( gpt5_codex_tools, - vec![ - "shell_command".to_string(), - "list_mcp_resources".to_string(), - "list_mcp_resource_templates".to_string(), - "read_mcp_resource".to_string(), - "update_plan".to_string(), - "request_user_input".to_string(), - "apply_patch".to_string(), - "web_search".to_string(), - "view_image".to_string() - ], + expected_default_tools( + "shell_command", + &[ + "list_mcp_resources", + "list_mcp_resource_templates", + "read_mcp_resource", + "update_plan", + "request_user_input", + "apply_patch", + "web_search", + "view_image", + ], + ), "gpt-5-codex should expose the apply_patch tool", ); let gpt51_codex_tools = collect_tool_identifiers_for_model("gpt-5.1-codex").await; assert_eq!( gpt51_codex_tools, - vec![ - "shell_command".to_string(), - "list_mcp_resources".to_string(), - "list_mcp_resource_templates".to_string(), - "read_mcp_resource".to_string(), - "update_plan".to_string(), - "request_user_input".to_string(), - "apply_patch".to_string(), - "web_search".to_string(), - "view_image".to_string() - ], + expected_default_tools( + "shell_command", + &[ + "list_mcp_resources", + "list_mcp_resource_templates", + "read_mcp_resource", + "update_plan", + "request_user_input", + "apply_patch", + "web_search", + "view_image", + ], + ), "gpt-5.1-codex should expose the apply_patch tool", ); let gpt5_tools = collect_tool_identifiers_for_model("gpt-5").await; assert_eq!( gpt5_tools, - vec![ - "shell".to_string(), - "list_mcp_resources".to_string(), - "list_mcp_resource_templates".to_string(), - "read_mcp_resource".to_string(), - "update_plan".to_string(), - "request_user_input".to_string(), - "web_search".to_string(), - "view_image".to_string() - ], + expected_default_tools( + "shell", + &[ + "list_mcp_resources", + "list_mcp_resource_templates", + "read_mcp_resource", + "update_plan", + "request_user_input", + "web_search", + "view_image", + ], + ), "gpt-5 should expose the apply_patch tool", ); let gpt51_tools = collect_tool_identifiers_for_model("gpt-5.1").await; assert_eq!( gpt51_tools, - vec![ - "shell_command".to_string(), - "list_mcp_resources".to_string(), - "list_mcp_resource_templates".to_string(), - "read_mcp_resource".to_string(), - "update_plan".to_string(), - "request_user_input".to_string(), - "apply_patch".to_string(), - "web_search".to_string(), - "view_image".to_string() - ], + expected_default_tools( + "shell_command", + &[ + "list_mcp_resources", + "list_mcp_resource_templates", + "read_mcp_resource", + "update_plan", + "request_user_input", + "apply_patch", + "web_search", + "view_image", + ], + ), "gpt-5.1 should expose the apply_patch tool", ); let exp_tools = collect_tool_identifiers_for_model("exp-5.1").await; diff --git a/codex-rs/core/tests/suite/pending_input.rs b/codex-rs/core/tests/suite/pending_input.rs index ad662d901b..1fcff4da3b 100644 --- a/codex-rs/core/tests/suite/pending_input.rs +++ b/codex-rs/core/tests/suite/pending_input.rs @@ -125,6 +125,11 @@ async fn injected_user_input_triggers_follow_up_request_with_deltas() { let _ = gate_completed_tx.send(()); + let _ = wait_for_event(&codex, |event| { + matches!(event, EventMsg::UserMessage(message) if message.message == "second prompt") + }) + .await; + wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; let requests = server.requests().await; diff --git a/codex-rs/core/tests/suite/permissions_messages.rs b/codex-rs/core/tests/suite/permissions_messages.rs index c54203b4f1..fc1f0fa0b6 100644 --- a/codex-rs/core/tests/suite/permissions_messages.rs +++ b/codex-rs/core/tests/suite/permissions_messages.rs @@ -43,16 +43,16 @@ fn permissions_texts(input: &[serde_json::Value]) -> Vec { .collect() } -fn sse_completed(id: &str) -> String { - sse(vec![ev_response_created(id), ev_completed(id)]) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn permissions_message_sent_once_on_start() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req = mount_sse_once(&server, sse_completed("resp-1")).await; + let req = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; let mut builder = test_codex().with_config(move |config| { config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest); @@ -84,8 +84,16 @@ async fn permissions_message_added_on_override_change() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let mut builder = test_codex().with_config(move |config| { config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest); @@ -148,8 +156,16 @@ async fn permissions_message_not_added_when_no_change() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let mut builder = test_codex().with_config(move |config| { config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest); @@ -197,9 +213,21 @@ async fn resume_replays_permissions_messages() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let _req2 = mount_sse_once(&server, sse_completed("resp-2")).await; - let req3 = mount_sse_once(&server, sse_completed("resp-3")).await; + let _req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let _req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; + let req3 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-3"), ev_completed("resp-3")]), + ) + .await; let mut builder = test_codex().with_config(|config| { config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest); @@ -279,10 +307,26 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; - let req3 = mount_sse_once(&server, sse_completed("resp-3")).await; - let req4 = mount_sse_once(&server, sse_completed("resp-4")).await; + let _req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; + let req3 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-3"), ev_completed("resp-3")]), + ) + .await; + let req4 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-4"), ev_completed("resp-4")]), + ) + .await; let mut builder = test_codex().with_config(|config| { config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest); @@ -404,7 +448,11 @@ async fn permissions_message_includes_writable_roots() -> Result<()> { skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req = mount_sse_once(&server, sse_completed("resp-1")).await; + let req = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; let writable = TempDir::new()?; let writable_root = AbsolutePathBuf::try_from(writable.path())?; let sandbox_policy = SandboxPolicy::WorkspaceWrite { diff --git a/codex-rs/core/tests/suite/personality.rs b/codex-rs/core/tests/suite/personality.rs index 87978ceb5a..4fdedd0798 100644 --- a/codex-rs/core/tests/suite/personality.rs +++ b/codex-rs/core/tests/suite/personality.rs @@ -19,12 +19,10 @@ use codex_protocol::openai_models::TruncationPolicyConfig; use codex_protocol::openai_models::default_input_modalities; use codex_protocol::user_input::UserInput; use core_test_support::load_default_config_for_test; -use core_test_support::responses::ev_completed; -use core_test_support::responses::ev_response_created; use core_test_support::responses::mount_models_once; use core_test_support::responses::mount_sse_once; use core_test_support::responses::mount_sse_sequence; -use core_test_support::responses::sse; +use core_test_support::responses::sse_completed; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; use core_test_support::test_codex::test_codex; @@ -42,10 +40,6 @@ const LOCAL_FRIENDLY_TEMPLATE: &str = "You optimize for team morale and being a supportive teammate as much as code quality."; const LOCAL_PRAGMATIC_TEMPLATE: &str = "You are a deeply pragmatic, effective software engineer."; -fn sse_completed(id: &str) -> String { - sse(vec![ev_response_created(id), ev_completed(id)]) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn personality_does_not_mutate_base_instructions_without_template() { let codex_home = TempDir::new().expect("create temp dir"); @@ -177,6 +171,111 @@ async fn config_personality_some_sets_instructions_template() -> anyhow::Result< Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn config_personality_none_sends_no_personality() -> anyhow::Result<()> { + skip_if_no_network!(Ok(())); + + let server = start_mock_server().await; + let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await; + let mut builder = test_codex() + .with_model("gpt-5.2-codex") + .with_config(|config| { + config.features.disable(Feature::RemoteModels); + config.features.enable(Feature::Personality); + config.personality = Some(Personality::None); + }); + let test = builder.build(&server).await?; + + test.codex + .submit(Op::UserTurn { + items: vec![UserInput::Text { + text: "hello".into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + cwd: test.cwd_path().to_path_buf(), + approval_policy: test.config.approval_policy.value(), + sandbox_policy: SandboxPolicy::ReadOnly, + model: test.session_configured.model.clone(), + effort: test.config.model_reasoning_effort, + summary: ReasoningSummary::Auto, + collaboration_mode: None, + personality: None, + }) + .await?; + + wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; + + let request = resp_mock.single_request(); + let instructions_text = request.instructions_text(); + assert!( + !instructions_text.contains(LOCAL_FRIENDLY_TEMPLATE), + "expected no friendly personality template, got: {instructions_text:?}" + ); + assert!( + !instructions_text.contains(LOCAL_PRAGMATIC_TEMPLATE), + "expected no pragmatic personality template, got: {instructions_text:?}" + ); + assert!( + !instructions_text.contains("{{ personality }}"), + "expected personality placeholder to be removed, got: {instructions_text:?}" + ); + + let developer_texts = request.message_input_texts("developer"); + assert!( + !developer_texts + .iter() + .any(|text| text.contains("")), + "did not expect a personality update message when personality is None" + ); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn default_personality_is_pragmatic_without_config_toml() -> anyhow::Result<()> { + skip_if_no_network!(Ok(())); + + let server = start_mock_server().await; + let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await; + let mut builder = test_codex() + .with_model("gpt-5.2-codex") + .with_config(|config| { + config.features.disable(Feature::RemoteModels); + config.features.enable(Feature::Personality); + }); + let test = builder.build(&server).await?; + + test.codex + .submit(Op::UserTurn { + items: vec![UserInput::Text { + text: "hello".into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + cwd: test.cwd_path().to_path_buf(), + approval_policy: test.config.approval_policy.value(), + sandbox_policy: SandboxPolicy::ReadOnly, + model: test.session_configured.model.clone(), + effort: test.config.model_reasoning_effort, + summary: ReasoningSummary::Auto, + collaboration_mode: None, + personality: None, + }) + .await?; + + wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; + + let request = resp_mock.single_request(); + let instructions_text = request.instructions_text(); + assert!( + instructions_text.contains(LOCAL_PRAGMATIC_TEMPLATE), + "expected default friendly template, got: {instructions_text:?}" + ); + + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); @@ -225,7 +324,7 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()> effort: None, summary: None, collaboration_mode: None, - personality: Some(Personality::Pragmatic), + personality: Some(Personality::Friendly), }) .await?; @@ -266,7 +365,7 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()> "expected personality update preamble, got {personality_text:?}" ); assert!( - personality_text.contains(LOCAL_PRAGMATIC_TEMPLATE), + personality_text.contains(LOCAL_FRIENDLY_TEMPLATE), "expected personality update to include the local pragmatic template, got: {personality_text:?}" ); @@ -783,7 +882,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::ReadOnly, - model: test.session_configured.model.clone(), + model: remote_slug.to_string(), effort: test.config.model_reasoning_effort, summary: ReasoningSummary::Auto, collaboration_mode: None, @@ -799,11 +898,11 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - approval_policy: None, sandbox_policy: None, windows_sandbox_level: None, - model: Some(remote_slug.to_string()), + model: None, effort: None, summary: None, collaboration_mode: None, - personality: Some(Personality::Pragmatic), + personality: Some(Personality::Friendly), }) .await?; @@ -843,7 +942,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - "expected personality update preamble, got {personality_text:?}" ); assert!( - personality_text.contains(remote_pragmatic_message), + personality_text.contains(remote_friendly_message), "expected personality update to include remote template, got: {personality_text:?}" ); diff --git a/codex-rs/core/tests/suite/personality_migration.rs b/codex-rs/core/tests/suite/personality_migration.rs index 492b4fdd22..2ca7aeeefd 100644 --- a/codex-rs/core/tests/suite/personality_migration.rs +++ b/codex-rs/core/tests/suite/personality_migration.rs @@ -42,6 +42,16 @@ async fn write_archived_session_with_user_event(codex_home: &Path) -> io::Result write_rollout_with_user_event(&dir, thread_id).await } +async fn write_session_with_meta_only(codex_home: &Path) -> io::Result<()> { + let thread_id = ThreadId::new(); + let dir = codex_home + .join(SESSIONS_SUBDIR) + .join("2025") + .join("01") + .join("01"); + write_rollout_with_meta_only(&dir, thread_id).await +} + async fn write_rollout_with_user_event(dir: &Path, thread_id: ThreadId) -> io::Result<()> { tokio::fs::create_dir_all(&dir).await?; let file_path = dir.join(format!("rollout-{TEST_TIMESTAMP}-{thread_id}.jsonl")); @@ -83,6 +93,40 @@ async fn write_rollout_with_user_event(dir: &Path, thread_id: ThreadId) -> io::R Ok(()) } +async fn write_rollout_with_meta_only(dir: &Path, thread_id: ThreadId) -> io::Result<()> { + tokio::fs::create_dir_all(&dir).await?; + let file_path = dir.join(format!("rollout-{TEST_TIMESTAMP}-{thread_id}.jsonl")); + let mut file = tokio::fs::File::create(&file_path).await?; + + let session_meta = SessionMetaLine { + meta: SessionMeta { + id: thread_id, + forked_from_id: None, + timestamp: TEST_TIMESTAMP.to_string(), + cwd: std::path::PathBuf::from("."), + originator: "test_originator".to_string(), + cli_version: "test_version".to_string(), + source: SessionSource::Cli, + model_provider: None, + base_instructions: None, + dynamic_tools: None, + }, + git: None, + }; + let meta_line = RolloutLine { + timestamp: TEST_TIMESTAMP.to_string(), + item: RolloutItem::SessionMeta(session_meta), + }; + + let meta_json = serde_json::to_string(&meta_line)?; + file.write_all(format!("{meta_json}\n").as_bytes()).await?; + Ok(()) +} + +fn parse_config_toml(contents: &str) -> io::Result { + toml::from_str(contents).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err)) +} + #[tokio::test] async fn migration_marker_exists_no_sessions_no_change() -> io::Result<()> { let temp = TempDir::new()?; @@ -135,6 +179,138 @@ async fn no_marker_sessions_sets_personality() -> io::Result<()> { Ok(()) } +#[tokio::test] +async fn no_marker_sessions_preserves_existing_config_fields() -> io::Result<()> { + let temp = TempDir::new()?; + write_session_with_user_event(temp.path()).await?; + tokio::fs::write(temp.path().join("config.toml"), "model = \"gpt-5-codex\"\n").await?; + let config_toml = read_config_toml(temp.path()).await?; + + let status = maybe_migrate_personality(temp.path(), &config_toml).await?; + + assert_eq!(status, PersonalityMigrationStatus::Applied); + let persisted = read_config_toml(temp.path()).await?; + assert_eq!(persisted.model, Some("gpt-5-codex".to_string())); + assert_eq!(persisted.personality, Some(Personality::Pragmatic)); + Ok(()) +} + +#[tokio::test] +async fn no_marker_meta_only_rollout_is_treated_as_no_sessions() -> io::Result<()> { + let temp = TempDir::new()?; + write_session_with_meta_only(temp.path()).await?; + + let status = maybe_migrate_personality(temp.path(), &ConfigToml::default()).await?; + + assert_eq!(status, PersonalityMigrationStatus::SkippedNoSessions); + assert_eq!( + tokio::fs::try_exists(temp.path().join(PERSONALITY_MIGRATION_FILENAME)).await?, + true + ); + assert_eq!( + tokio::fs::try_exists(temp.path().join("config.toml")).await?, + false + ); + Ok(()) +} + +#[tokio::test] +async fn no_marker_explicit_global_personality_skips_migration() -> io::Result<()> { + let temp = TempDir::new()?; + write_session_with_user_event(temp.path()).await?; + let config_toml = parse_config_toml("personality = \"friendly\"\n")?; + + let status = maybe_migrate_personality(temp.path(), &config_toml).await?; + + assert_eq!( + status, + PersonalityMigrationStatus::SkippedExplicitPersonality + ); + assert_eq!( + tokio::fs::try_exists(temp.path().join(PERSONALITY_MIGRATION_FILENAME)).await?, + true + ); + assert_eq!( + tokio::fs::try_exists(temp.path().join("config.toml")).await?, + false + ); + Ok(()) +} + +#[tokio::test] +async fn no_marker_profile_personality_skips_migration() -> io::Result<()> { + let temp = TempDir::new()?; + write_session_with_user_event(temp.path()).await?; + let config_toml = parse_config_toml( + r#" +profile = "work" + +[profiles.work] +personality = "friendly" +"#, + )?; + + let status = maybe_migrate_personality(temp.path(), &config_toml).await?; + + assert_eq!( + status, + PersonalityMigrationStatus::SkippedExplicitPersonality + ); + assert_eq!( + tokio::fs::try_exists(temp.path().join(PERSONALITY_MIGRATION_FILENAME)).await?, + true + ); + assert_eq!( + tokio::fs::try_exists(temp.path().join("config.toml")).await?, + false + ); + Ok(()) +} + +#[tokio::test] +async fn marker_short_circuits_invalid_profile_resolution() -> io::Result<()> { + let temp = TempDir::new()?; + tokio::fs::write(temp.path().join(PERSONALITY_MIGRATION_FILENAME), "v1\n").await?; + let config_toml = parse_config_toml("profile = \"missing\"\n")?; + + let status = maybe_migrate_personality(temp.path(), &config_toml).await?; + + assert_eq!(status, PersonalityMigrationStatus::SkippedMarker); + Ok(()) +} + +#[tokio::test] +async fn invalid_selected_profile_returns_error_and_does_not_write_marker() -> io::Result<()> { + let temp = TempDir::new()?; + let config_toml = parse_config_toml("profile = \"missing\"\n")?; + + let err = maybe_migrate_personality(temp.path(), &config_toml) + .await + .expect_err("missing profile should fail"); + + assert_eq!(err.kind(), io::ErrorKind::InvalidData); + assert_eq!( + tokio::fs::try_exists(temp.path().join(PERSONALITY_MIGRATION_FILENAME)).await?, + false + ); + Ok(()) +} + +#[tokio::test] +async fn applied_migration_is_idempotent_on_second_run() -> io::Result<()> { + let temp = TempDir::new()?; + write_session_with_user_event(temp.path()).await?; + + let first_status = maybe_migrate_personality(temp.path(), &ConfigToml::default()).await?; + let second_status = maybe_migrate_personality(temp.path(), &ConfigToml::default()).await?; + + assert_eq!(first_status, PersonalityMigrationStatus::Applied); + assert_eq!(second_status, PersonalityMigrationStatus::SkippedMarker); + let persisted = read_config_toml(temp.path()).await?; + assert_eq!(persisted.personality, Some(Personality::Pragmatic)); + Ok(()) +} + #[tokio::test] async fn no_marker_archived_sessions_sets_personality() -> io::Result<()> { let temp = TempDir::new()?; diff --git a/codex-rs/core/tests/suite/prompt_caching.rs b/codex-rs/core/tests/suite/prompt_caching.rs index bf1819cbf1..9b1548591b 100644 --- a/codex-rs/core/tests/suite/prompt_caching.rs +++ b/codex-rs/core/tests/suite/prompt_caching.rs @@ -18,8 +18,10 @@ use codex_protocol::config_types::WebSearchMode; use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::user_input::UserInput; use codex_utils_absolute_path::AbsolutePathBuf; -use core_test_support::load_sse_fixture_with_id; +use core_test_support::responses::ev_completed; +use core_test_support::responses::ev_response_created; use core_test_support::responses::mount_sse_once; +use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; use core_test_support::test_codex::TestCodex; @@ -46,11 +48,6 @@ fn default_env_context_str(cwd: &str, shell: &Shell) -> String { ) } -/// Build minimal SSE stream with completed marker using the JSON fixture. -fn sse_completed(id: &str) -> String { - load_sse_fixture_with_id("../fixtures/completed_template.json", id) -} - fn assert_tool_names(body: &serde_json::Value, expected_names: &[&str]) { assert_eq!( body["tools"] @@ -79,8 +76,16 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> { use pretty_assertions::assert_eq; let server = start_mock_server().await; - let req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let TestCodex { codex, @@ -131,8 +136,12 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> { .await?; wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await; - let expected_tools_names = vec![ - "shell_command", + let mut expected_tools_names = if cfg!(windows) { + vec!["shell_command"] + } else { + vec!["exec_command", "write_stdin"] + }; + expected_tools_names.extend([ "list_mcp_resources", "list_mcp_resource_templates", "read_mcp_resource", @@ -141,7 +150,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> { "apply_patch", "web_search", "view_image", - ]; + ]); let body0 = req1.single_request().body_json(); let expected_instructions = if expected_tools_names.contains(&"apply_patch") { @@ -172,8 +181,16 @@ async fn codex_mini_latest_tools() -> anyhow::Result<()> { use pretty_assertions::assert_eq; let server = start_mock_server().await; - let req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let TestCodex { codex, .. } = test_codex() .with_config(|config| { @@ -238,8 +255,16 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests use pretty_assertions::assert_eq; let server = start_mock_server().await; - let req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let TestCodex { codex, config, .. } = test_codex() .with_config(|config| { @@ -315,8 +340,16 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an use pretty_assertions::assert_eq; let server = start_mock_server().await; - let req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let TestCodex { codex, .. } = test_codex() .with_config(|config| { @@ -351,7 +384,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an approval_policy: Some(AskForApproval::Never), sandbox_policy: Some(new_policy.clone()), windows_sandbox_level: None, - model: Some("o3".to_string()), + model: None, effort: Some(Some(ReasoningEffort::High)), summary: Some(ReasoningSummary::Detailed), collaboration_mode: None, @@ -407,7 +440,11 @@ async fn override_before_first_turn_emits_environment_context() -> anyhow::Resul skip_if_no_network!(Ok(())); let server = start_mock_server().await; - let req = mount_sse_once(&server, sse_completed("resp-1")).await; + let req = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; let TestCodex { codex, .. } = test_codex().build(&server).await?; @@ -542,8 +579,16 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res use pretty_assertions::assert_eq; let server = start_mock_server().await; - let req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let TestCodex { codex, .. } = test_codex() .with_config(|config| { @@ -631,9 +676,21 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res expected_permissions_msg_2, expected_permissions_msg, "expected updated permissions message after per-turn override" ); + let expected_model_switch_msg = body2["input"][body1_input.len() + 2].clone(); + assert_eq!( + expected_model_switch_msg["role"].as_str(), + Some("developer") + ); + assert!( + expected_model_switch_msg["content"][0]["text"] + .as_str() + .is_some_and(|text| text.contains("")), + "expected model switch message after model override: {expected_model_switch_msg:?}" + ); let mut expected_body2 = body1_input.to_vec(); expected_body2.push(expected_env_msg_2); expected_body2.push(expected_permissions_msg_2); + expected_body2.push(expected_model_switch_msg); expected_body2.push(expected_user_message_2); assert_eq!(body2["input"], serde_json::Value::Array(expected_body2)); @@ -646,8 +703,16 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a use pretty_assertions::assert_eq; let server = start_mock_server().await; - let req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let TestCodex { codex, @@ -747,8 +812,16 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu let server = start_mock_server().await; - let req1 = mount_sse_once(&server, sse_completed("resp-1")).await; - let req2 = mount_sse_once(&server, sse_completed("resp-2")).await; + let req1 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + ) + .await; + let req2 = mount_sse_once( + &server, + sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ) + .await; let TestCodex { codex, config, @@ -831,6 +904,17 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu expected_permissions_msg_2, expected_permissions_msg, "expected updated permissions message after policy change" ); + let expected_model_switch_msg = body2["input"][body1_input.len() + 1].clone(); + assert_eq!( + expected_model_switch_msg["role"].as_str(), + Some("developer") + ); + assert!( + expected_model_switch_msg["content"][0]["text"] + .as_str() + .is_some_and(|text| text.contains("")), + "expected model switch message after model override: {expected_model_switch_msg:?}" + ); let expected_user_message_2 = text_user_input("hello 2".to_string()); let expected_input_2 = serde_json::Value::Array(vec![ expected_permissions_msg, @@ -838,6 +922,7 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu expected_env_msg_1, expected_user_message_1, expected_permissions_msg_2, + expected_model_switch_msg, expected_user_message_2, ]); assert_eq!(body2["input"], expected_input_2); diff --git a/codex-rs/core/tests/suite/resume.rs b/codex-rs/core/tests/suite/resume.rs index a7be42f430..c0bdcd4fe2 100644 --- a/codex-rs/core/tests/suite/resume.rs +++ b/codex-rs/core/tests/suite/resume.rs @@ -9,6 +9,7 @@ use core_test_support::responses::ev_completed; use core_test_support::responses::ev_reasoning_item; use core_test_support::responses::ev_response_created; use core_test_support::responses::mount_sse_once; +use core_test_support::responses::mount_sse_sequence; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; @@ -182,12 +183,22 @@ async fn resume_switches_models_preserves_base_instructions() -> Result<()> { .unwrap_or_default() .to_string(); - let resumed_sse = sse(vec![ - ev_response_created("resp-resume"), - ev_assistant_message("msg-2", "Resumed turn"), - ev_completed("resp-resume"), - ]); - let resumed_mock = mount_sse_once(&server, resumed_sse).await; + let resumed_mock = mount_sse_sequence( + &server, + vec![ + sse(vec![ + ev_response_created("resp-resume-1"), + ev_assistant_message("msg-2", "Resumed turn"), + ev_completed("resp-resume-1"), + ]), + sse(vec![ + ev_response_created("resp-resume-2"), + ev_assistant_message("msg-3", "Second resumed turn"), + ev_completed("resp-resume-2"), + ]), + ], + ) + .await; let mut resume_builder = test_codex().with_config(|config| { config.model = Some("gpt-5.2-codex".to_string()); @@ -208,13 +219,139 @@ async fn resume_switches_models_preserves_base_instructions() -> Result<()> { }) .await; - let resumed_body = resumed_mock.single_request().body_json(); - let resumed_instructions = resumed_body - .get("instructions") - .and_then(|v| v.as_str()) - .unwrap_or_default() - .to_string(); - assert_eq!(resumed_instructions, initial_instructions); + resumed + .codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: "Second turn after resume".into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + }) + .await?; + wait_for_event(&resumed.codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + let requests = resumed_mock.requests(); + assert_eq!(requests.len(), 2, "expected two resumed requests"); + + let first_resumed = &requests[0]; + assert_eq!(first_resumed.instructions_text(), initial_instructions); + let first_developer_texts = first_resumed.message_input_texts("developer"); + let first_model_switch_count = first_developer_texts + .iter() + .filter(|text| text.contains("")) + .count(); + assert!( + first_model_switch_count >= 1, + "expected model switch message on first post-resume turn" + ); + + let second_resumed = &requests[1]; + assert_eq!(second_resumed.instructions_text(), initial_instructions); + let second_developer_texts = second_resumed.message_input_texts("developer"); + let second_model_switch_count = second_developer_texts + .iter() + .filter(|text| text.contains("")) + .count(); + assert_eq!( + second_model_switch_count, 1, + "did not expect duplicate model switch message after first post-resume turn" + ); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn resume_model_switch_is_not_duplicated_after_pre_turn_override() -> Result<()> { + skip_if_no_network!(Ok(())); + + let server = start_mock_server().await; + let mut builder = test_codex().with_config(|config| { + config.model = Some("gpt-5.2".to_string()); + }); + let initial = builder.build(&server).await?; + let codex = Arc::clone(&initial.codex); + let home = initial.home.clone(); + let rollout_path = initial + .session_configured + .rollout_path + .clone() + .expect("rollout path"); + + let initial_mock = mount_sse_once( + &server, + sse(vec![ + ev_response_created("resp-initial"), + ev_assistant_message("msg-1", "Completed first turn"), + ev_completed("resp-initial"), + ]), + ) + .await; + codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: "Record initial instructions".into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + }) + .await?; + wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await; + let _ = initial_mock.single_request(); + + let resumed_mock = mount_sse_once( + &server, + sse(vec![ + ev_response_created("resp-resume"), + ev_assistant_message("msg-2", "Resumed turn"), + ev_completed("resp-resume"), + ]), + ) + .await; + + let mut resume_builder = test_codex().with_config(|config| { + config.model = Some("gpt-5.2-codex".to_string()); + }); + let resumed = resume_builder.resume(&server, home, rollout_path).await?; + resumed + .codex + .submit(Op::OverrideTurnContext { + cwd: None, + approval_policy: None, + sandbox_policy: None, + windows_sandbox_level: None, + model: Some("gpt-5.1-codex-max".to_string()), + effort: None, + summary: None, + collaboration_mode: None, + personality: None, + }) + .await?; + resumed + .codex + .submit(Op::UserInput { + items: vec![UserInput::Text { + text: "first turn after override".into(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + }) + .await?; + wait_for_event(&resumed.codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + let request = resumed_mock.single_request(); + let developer_texts = request.message_input_texts("developer"); + let model_switch_count = developer_texts + .iter() + .filter(|text| text.contains("")) + .count(); + assert_eq!(model_switch_count, 1); Ok(()) } diff --git a/codex-rs/core/tests/suite/rollout_list_find.rs b/codex-rs/core/tests/suite/rollout_list_find.rs index 8786c9be21..236a70279b 100644 --- a/codex-rs/core/tests/suite/rollout_list_find.rs +++ b/codex-rs/core/tests/suite/rollout_list_find.rs @@ -59,6 +59,7 @@ async fn upsert_thread_metadata(codex_home: &Path, thread_id: ThreadId, rollout_ let runtime = StateRuntime::init(codex_home.to_path_buf(), "test-provider".to_string(), None) .await .unwrap(); + runtime.mark_backfill_complete(None).await.unwrap(); let mut builder = ThreadMetadataBuilder::new( thread_id, rollout_path, @@ -104,9 +105,11 @@ async fn find_prefers_sqlite_path_by_id() { let home = TempDir::new().unwrap(); let id = Uuid::new_v4(); let thread_id = ThreadId::from_string(&id.to_string()).unwrap(); - let db_path = home - .path() - .join("sessions/2030/12/30/rollout-2030-12-30T00-00-00-db.jsonl"); + let db_path = home.path().join(format!( + "sessions/2030/12/30/rollout-2030-12-30T00-00-00-{id}.jsonl" + )); + std::fs::create_dir_all(db_path.parent().unwrap()).unwrap(); + std::fs::write(&db_path, "").unwrap(); write_minimal_rollout_with_id(home.path(), id); upsert_thread_metadata(home.path(), thread_id, db_path.clone()).await; diff --git a/codex-rs/core/tests/suite/sqlite_state.rs b/codex-rs/core/tests/suite/sqlite_state.rs index 218da34825..688b034ac7 100644 --- a/codex-rs/core/tests/suite/sqlite_state.rs +++ b/codex-rs/core/tests/suite/sqlite_state.rs @@ -9,8 +9,6 @@ use codex_protocol::protocol::SessionMeta; use codex_protocol::protocol::SessionMetaLine; use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::UserMessageEvent; -use codex_state::STATE_DB_FILENAME; -use core_test_support::load_sse_fixture_with_id; use core_test_support::responses; use core_test_support::responses::ev_completed; use core_test_support::responses::ev_function_call; @@ -25,10 +23,6 @@ use tokio::time::Duration; use tracing_subscriber::prelude::*; use uuid::Uuid; -fn sse_completed(id: &str) -> String { - load_sse_fixture_with_id("../fixtures/completed_template.json", id) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn new_thread_is_recorded_in_state_db() -> Result<()> { let server = start_mock_server().await; @@ -39,7 +33,7 @@ async fn new_thread_is_recorded_in_state_db() -> Result<()> { let thread_id = test.session_configured.session_id; let rollout_path = test.codex.rollout_path().expect("rollout path"); - let db_path = test.config.codex_home.join(STATE_DB_FILENAME); + let db_path = codex_state::state_db_path(test.config.codex_home.as_path()); for _ in 0..100 { if tokio::fs::try_exists(&db_path).await.unwrap_or(false) { @@ -149,7 +143,7 @@ async fn backfill_scans_existing_rollouts() -> Result<()> { let test = builder.build(&server).await?; - let db_path = test.config.codex_home.join(STATE_DB_FILENAME); + let db_path = codex_state::state_db_path(test.config.codex_home.as_path()); let rollout_path = test.config.codex_home.join(&rollout_rel_path); let default_provider = test.config.model_provider_id.clone(); @@ -175,7 +169,7 @@ async fn backfill_scans_existing_rollouts() -> Result<()> { assert_eq!(metadata.id, thread_id); assert_eq!(metadata.rollout_path, rollout_path); assert_eq!(metadata.model_provider, default_provider); - assert!(metadata.has_user_event); + assert!(metadata.first_user_message.is_some()); let mut stored_tools = None; for _ in 0..40 { @@ -196,7 +190,10 @@ async fn user_messages_persist_in_state_db() -> Result<()> { let server = start_mock_server().await; mount_sse_sequence( &server, - vec![sse_completed("resp-1"), sse_completed("resp-2")], + vec![ + responses::sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]), + responses::sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]), + ], ) .await; @@ -205,7 +202,7 @@ async fn user_messages_persist_in_state_db() -> Result<()> { }); let test = builder.build(&server).await?; - let db_path = test.config.codex_home.join(STATE_DB_FILENAME); + let db_path = codex_state::state_db_path(test.config.codex_home.as_path()); for _ in 0..100 { if tokio::fs::try_exists(&db_path).await.unwrap_or(false) { break; @@ -224,7 +221,7 @@ async fn user_messages_persist_in_state_db() -> Result<()> { metadata = db.get_thread(thread_id).await?; if metadata .as_ref() - .map(|entry| entry.has_user_event) + .map(|entry| entry.first_user_message.is_some()) .unwrap_or(false) { break; @@ -233,7 +230,7 @@ async fn user_messages_persist_in_state_db() -> Result<()> { } let metadata = metadata.expect("thread should exist in state db"); - assert!(metadata.has_user_event); + assert!(metadata.first_user_message.is_some()); Ok(()) } diff --git a/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs b/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs index ec18816235..3562ec2500 100644 --- a/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs +++ b/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs @@ -3,7 +3,9 @@ use codex_core::WireApi; use codex_core::protocol::EventMsg; use codex_core::protocol::Op; use codex_protocol::user_input::UserInput; -use core_test_support::load_sse_fixture_with_id; +use core_test_support::responses::ev_completed; +use core_test_support::responses::ev_response_created; +use core_test_support::responses::sse; use core_test_support::skip_if_no_network; use core_test_support::test_codex::TestCodex; use core_test_support::test_codex::test_codex; @@ -15,10 +17,6 @@ use wiremock::matchers::body_string_contains; use wiremock::matchers::method; use wiremock::matchers::path; -fn sse_completed(id: &str) -> String { - load_sse_fixture_with_id("../fixtures/completed_template.json", id) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn continue_after_stream_error() { skip_if_no_network!(); @@ -46,7 +44,13 @@ async fn continue_after_stream_error() { let ok = ResponseTemplate::new(200) .insert_header("content-type", "text/event-stream") - .set_body_raw(sse_completed("resp_ok2"), "text/event-stream"); + .set_body_raw( + sse(vec![ + ev_response_created("resp_ok2"), + ev_completed("resp_ok2"), + ]), + "text/event-stream", + ); Mock::given(method("POST")) .and(path("/v1/responses")) diff --git a/codex-rs/core/tests/suite/stream_no_completed.rs b/codex-rs/core/tests/suite/stream_no_completed.rs index 68b7763eca..6528e6277d 100644 --- a/codex-rs/core/tests/suite/stream_no_completed.rs +++ b/codex-rs/core/tests/suite/stream_no_completed.rs @@ -7,7 +7,9 @@ use codex_core::protocol::EventMsg; use codex_core::protocol::Op; use codex_protocol::user_input::UserInput; use core_test_support::load_sse_fixture; -use core_test_support::load_sse_fixture_with_id; +use core_test_support::responses::ev_completed; +use core_test_support::responses::ev_response_created; +use core_test_support::responses::sse; use core_test_support::skip_if_no_network; use core_test_support::test_codex::TestCodex; use core_test_support::test_codex::test_codex; @@ -24,10 +26,6 @@ fn sse_incomplete() -> String { load_sse_fixture("tests/fixtures/incomplete_sse.json") } -fn sse_completed(id: &str) -> String { - load_sse_fixture_with_id("../fixtures/completed_template.json", id) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn retries_on_early_close() { skip_if_no_network!(); @@ -48,7 +46,13 @@ async fn retries_on_early_close() { } else { ResponseTemplate::new(200) .insert_header("content-type", "text/event-stream") - .set_body_raw(sse_completed("resp_ok"), "text/event-stream") + .set_body_raw( + sse(vec![ + ev_response_created("resp_ok"), + ev_completed("resp_ok"), + ]), + "text/event-stream", + ) } } } @@ -67,7 +71,7 @@ async fn retries_on_early_close() { name: "openai".into(), base_url: Some(format!("{}/v1", server.uri())), // Environment variable that should exist in the test environment. - // ModelClientSession will return an error if the environment variable for the + // ModelClient will return an error if the environment variable for the // provider is not set. env_key: Some("PATH".into()), env_key_instructions: None, diff --git a/codex-rs/core/tests/suite/tool_parallelism.rs b/codex-rs/core/tests/suite/tool_parallelism.rs index 955b2f7ec3..0df8f8cbc9 100644 --- a/codex-rs/core/tests/suite/tool_parallelism.rs +++ b/codex-rs/core/tests/suite/tool_parallelism.rs @@ -149,6 +149,8 @@ async fn shell_tools_run_in_parallel() -> anyhow::Result<()> { let shell_args = json!({ "command": "sleep 0.3", + // Avoid user-specific shell startup cost (e.g. zsh profile scripts) in timing assertions. + "login": false, "timeout_ms": 1_000, }); let args_one = serde_json::to_string(&shell_args)?; diff --git a/codex-rs/core/tests/suite/user_shell_cmd.rs b/codex-rs/core/tests/suite/user_shell_cmd.rs index 45c91126d1..88a5caba4c 100644 --- a/codex-rs/core/tests/suite/user_shell_cmd.rs +++ b/codex-rs/core/tests/suite/user_shell_cmd.rs @@ -1,5 +1,6 @@ use anyhow::Context; use codex_core::features::Feature; +use codex_core::protocol::AskForApproval; use codex_core::protocol::EventMsg; use codex_core::protocol::ExecCommandEndEvent; use codex_core::protocol::ExecCommandSource; @@ -7,6 +8,8 @@ use codex_core::protocol::ExecOutputStream; use codex_core::protocol::Op; use codex_core::protocol::SandboxPolicy; use codex_core::protocol::TurnAbortReason; +use codex_protocol::config_types::ReasoningSummary; +use codex_protocol::user_input::UserInput; use core_test_support::assert_regex_match; use core_test_support::responses; use core_test_support::responses::ev_assistant_message; @@ -23,6 +26,8 @@ use core_test_support::wait_for_event_match; use regex_lite::escape; use std::path::PathBuf; use tempfile::TempDir; +use tokio::time::Duration; +use tokio::time::timeout; #[tokio::test] async fn user_shell_cmd_ls_and_cat_in_temp_dir() { @@ -119,6 +124,115 @@ async fn user_shell_cmd_can_be_interrupted() { assert_eq!(ev.reason, TurnAbortReason::Interrupted); } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn user_shell_command_does_not_replace_active_turn() -> anyhow::Result<()> { + let server = start_mock_server().await; + let mut builder = test_codex().with_model("gpt-5.1"); + let fixture = builder.build(&server).await?; + + let call_id = "active-turn-shell-call"; + let args = if cfg!(windows) { + serde_json::json!({ + "command": "Start-Sleep -Seconds 2; Write-Output model-shell", + "timeout_ms": 10_000, + }) + } else { + serde_json::json!({ + "command": "sleep 2; echo model-shell", + "timeout_ms": 10_000, + }) + }; + let first = sse(vec![ + ev_response_created("resp-1"), + ev_function_call(call_id, "shell_command", &serde_json::to_string(&args)?), + ev_completed("resp-1"), + ]); + let second = sse(vec![ + ev_assistant_message("msg-1", "done"), + ev_completed("resp-2"), + ]); + let mock = responses::mount_sse_sequence(&server, vec![first, second]).await; + + fixture + .codex + .submit(Op::UserTurn { + items: vec![UserInput::Text { + text: "run model shell command".to_string(), + text_elements: Vec::new(), + }], + final_output_json_schema: None, + cwd: fixture.cwd.path().to_path_buf(), + approval_policy: AskForApproval::Never, + sandbox_policy: SandboxPolicy::DangerFullAccess, + model: fixture.session_configured.model.clone(), + effort: None, + summary: ReasoningSummary::Auto, + collaboration_mode: None, + personality: None, + }) + .await?; + + let _ = wait_for_event_match(&fixture.codex, |ev| match ev { + EventMsg::ExecCommandBegin(event) if event.source == ExecCommandSource::Agent => { + Some(event.clone()) + } + _ => None, + }) + .await; + + #[cfg(windows)] + let user_shell_command = "Write-Output user-shell".to_string(); + #[cfg(not(windows))] + let user_shell_command = "printf user-shell".to_string(); + fixture + .codex + .submit(Op::RunUserShellCommand { + command: user_shell_command, + }) + .await?; + + let mut saw_replaced_abort = false; + let mut saw_user_shell_end = false; + let mut saw_turn_complete = false; + for _ in 0..200 { + let event = timeout(Duration::from_secs(20), fixture.codex.next_event()) + .await + .context("timed out waiting for event")? + .context("event stream ended unexpectedly")?; + match event.msg { + EventMsg::TurnAborted(ev) if ev.reason == TurnAbortReason::Replaced => { + saw_replaced_abort = true; + } + EventMsg::ExecCommandEnd(ev) if ev.source == ExecCommandSource::UserShell => { + saw_user_shell_end = true; + } + EventMsg::TurnComplete(_) => { + saw_turn_complete = true; + break; + } + _ => {} + } + } + + assert!(saw_turn_complete, "expected turn to complete"); + assert!( + saw_user_shell_end, + "expected user shell command to finish while turn was active" + ); + assert!( + !saw_replaced_abort, + "user shell command should not replace the active turn" + ); + + assert_eq!( + mock.requests().len(), + 2, + "active turn should continue and issue the follow-up model request" + ); + + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn user_shell_command_history_is_persisted_and_shared_with_model() -> anyhow::Result<()> { let server = responses::start_mock_server().await; diff --git a/codex-rs/core/tests/suite/web_search.rs b/codex-rs/core/tests/suite/web_search.rs index 9397e88edb..edcbfd35d3 100644 --- a/codex-rs/core/tests/suite/web_search.rs +++ b/codex-rs/core/tests/suite/web_search.rs @@ -1,11 +1,8 @@ #![allow(clippy::unwrap_used)] -use codex_core::WireApi; -use codex_core::built_in_model_providers; use codex_core::features::Feature; use codex_core::protocol::SandboxPolicy; use codex_protocol::config_types::WebSearchMode; -use core_test_support::load_sse_fixture_with_id; use core_test_support::responses; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; @@ -13,10 +10,6 @@ use core_test_support::test_codex::test_codex; use pretty_assertions::assert_eq; use serde_json::Value; -fn sse_completed(id: &str) -> String { - load_sse_fixture_with_id("../fixtures/completed_template.json", id) -} - #[allow(clippy::expect_used)] fn find_web_search_tool(body: &Value) -> &Value { body["tools"] @@ -27,21 +20,15 @@ fn find_web_search_tool(body: &Value) -> &Value { .expect("tools should include a web_search tool") } -#[allow(clippy::expect_used)] -fn has_web_search_tool(body: &Value) -> bool { - body["tools"] - .as_array() - .expect("request body should include tools array") - .iter() - .any(|tool| tool.get("type").and_then(Value::as_str) == Some("web_search")) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn web_search_mode_cached_sets_external_web_access_false() { skip_if_no_network!(); let server = start_mock_server().await; - let sse = sse_completed("resp-1"); + let sse = responses::sse(vec![ + responses::ev_response_created("resp-1"), + responses::ev_completed("resp-1"), + ]); let resp_mock = responses::mount_sse_once(&server, sse).await; let mut builder = test_codex() @@ -72,7 +59,10 @@ async fn web_search_mode_takes_precedence_over_legacy_flags() { skip_if_no_network!(); let server = start_mock_server().await; - let sse = sse_completed("resp-1"); + let sse = responses::sse(vec![ + responses::ev_response_created("resp-1"), + responses::ev_completed("resp-1"), + ]); let resp_mock = responses::mount_sse_once(&server, sse).await; let mut builder = test_codex() @@ -104,7 +94,10 @@ async fn web_search_mode_defaults_to_cached_when_unset() { skip_if_no_network!(); let server = start_mock_server().await; - let sse = sse_completed("resp-1"); + let sse = responses::sse(vec![ + responses::ev_response_created("resp-1"), + responses::ev_completed("resp-1"), + ]); let resp_mock = responses::mount_sse_once(&server, sse).await; let mut builder = test_codex() @@ -139,7 +132,16 @@ async fn web_search_mode_updates_between_turns_with_sandbox_policy() { let server = start_mock_server().await; let resp_mock = responses::mount_sse_sequence( &server, - vec![sse_completed("resp-1"), sse_completed("resp-2")], + vec![ + responses::sse(vec![ + responses::ev_response_created("resp-1"), + responses::ev_completed("resp-1"), + ]), + responses::sse(vec![ + responses::ev_response_created("resp-2"), + responses::ev_completed("resp-2"), + ]), + ], ) .await; @@ -185,45 +187,3 @@ async fn web_search_mode_updates_between_turns_with_sandbox_policy() { "danger-full-access policy should default web_search to live" ); } - -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn web_search_mode_defaults_to_disabled_for_azure_responses() { - skip_if_no_network!(); - - let server = start_mock_server().await; - let sse = sse_completed("resp-1"); - let resp_mock = responses::mount_sse_once(&server, sse).await; - - let mut builder = test_codex() - .with_model("gpt-5-codex") - .with_config(|config| { - let base_url = config.model_provider.base_url.clone(); - let mut provider = built_in_model_providers()["openai"].clone(); - provider.name = "Azure".to_string(); - provider.base_url = base_url; - provider.wire_api = WireApi::Responses; - config.model_provider_id = provider.name.clone(); - config.model_provider = provider; - config.web_search_mode = None; - config.features.disable(Feature::WebSearchCached); - config.features.disable(Feature::WebSearchRequest); - }); - let test = builder - .build(&server) - .await - .expect("create test Codex conversation"); - - test.submit_turn_with_policy( - "hello azure default web search", - SandboxPolicy::DangerFullAccess, - ) - .await - .expect("submit turn"); - - let body = resp_mock.single_request().body_json(); - assert_eq!( - has_web_search_tool(&body), - false, - "azure responses requests should disable web_search by default" - ); -} diff --git a/codex-rs/docs/codex_mcp_interface.md b/codex-rs/docs/codex_mcp_interface.md index edd693d5ed..8b1b5f7045 100644 --- a/codex-rs/docs/codex_mcp_interface.md +++ b/codex-rs/docs/codex_mcp_interface.md @@ -73,6 +73,8 @@ Send input to the active turn: - `sendUserMessage` → enqueue items to the conversation - `sendUserTurn` → structured turn with explicit `cwd`, `approvalPolicy`, `sandboxPolicy`, `model`, optional `effort`, `summary`, optional `personality`, and optional `outputSchema` (JSON Schema for the final assistant message) +Valid `personality` values are `friendly`, `pragmatic`, and `none`. When `none` is selected, the personality placeholder is replaced with an empty string. + For v2 threads, `turn/start` also accepts `outputSchema` to constrain the final assistant message for that turn. Interrupt a running turn: `interruptConversation`. @@ -100,6 +102,7 @@ Each response yields: - `defaultReasoningEffort` – suggested effort for the UI - `supportsPersonality` – whether the model supports personality-specific instructions - `isDefault` – whether the model is recommended for most users + - `upgrade` – optional recommended upgrade model id - `nextCursor` – pass into the next request to continue paging (optional) ## Collaboration modes (experimental) diff --git a/codex-rs/docs/protocol_v1.md b/codex-rs/docs/protocol_v1.md index 8f68ecc9a6..4d4e5c147c 100644 --- a/codex-rs/docs/protocol_v1.md +++ b/codex-rs/docs/protocol_v1.md @@ -72,6 +72,9 @@ For complete documentation of the `Op` and `EventMsg` variants, refer to [protoc - `Op::UserInputAnswer` – Provide answers for a `request_user_input` tool call - `Op::ListSkills` – Request skills for one or more cwd values (optionally `force_reload`) - `Op::UserTurn` and `Op::OverrideTurnContext` accept an optional `personality` override that updates the model’s communication style + +Valid `personality` values are `friendly`, `pragmatic`, and `none`. When `none` is selected, the personality placeholder is replaced with an empty string. + - `EventMsg` - `EventMsg::AgentMessage` – Messages from the `Model` - `EventMsg::AgentMessageContentDelta` – Streaming assistant text diff --git a/codex-rs/exec-server/src/posix/escalate_server.rs b/codex-rs/exec-server/src/posix/escalate_server.rs index ef991ce8cd..a2f247daae 100644 --- a/codex-rs/exec-server/src/posix/escalate_server.rs +++ b/codex-rs/exec-server/src/posix/escalate_server.rs @@ -95,6 +95,7 @@ impl EscalateServer { &sandbox_state.sandbox_policy, &sandbox_state.sandbox_cwd, &sandbox_state.codex_linux_sandbox_exe, + sandbox_state.use_linux_sandbox_bwrap, None, ) .await?; diff --git a/codex-rs/exec-server/src/posix/mcp.rs b/codex-rs/exec-server/src/posix/mcp.rs index 620d332e71..e48b70a2a2 100644 --- a/codex-rs/exec-server/src/posix/mcp.rs +++ b/codex-rs/exec-server/src/posix/mcp.rs @@ -126,6 +126,7 @@ impl ExecTool { sandbox_policy: SandboxPolicy::ReadOnly, codex_linux_sandbox_exe: None, sandbox_cwd: PathBuf::from(¶ms.workdir), + use_linux_sandbox_bwrap: false, }); let escalate_server = EscalateServer::new( self.bash_path.clone(), diff --git a/codex-rs/exec-server/tests/common/lib.rs b/codex-rs/exec-server/tests/common/lib.rs index 562d3504f6..66bb4b7f9c 100644 --- a/codex-rs/exec-server/tests/common/lib.rs +++ b/codex-rs/exec-server/tests/common/lib.rs @@ -91,6 +91,7 @@ where sandbox_policy: SandboxPolicy::ReadOnly, codex_linux_sandbox_exe, sandbox_cwd: sandbox_cwd.as_ref().to_path_buf(), + use_linux_sandbox_bwrap: false, }; send_sandbox_state_update(sandbox_state, service).await } @@ -118,6 +119,7 @@ where }, codex_linux_sandbox_exe, sandbox_cwd: writable_folder.as_ref().to_path_buf(), + use_linux_sandbox_bwrap: false, }; send_sandbox_state_update(sandbox_state, service).await } diff --git a/codex-rs/exec/src/cli.rs b/codex-rs/exec/src/cli.rs index c27c39f010..5d7a9ba725 100644 --- a/codex-rs/exec/src/cli.rs +++ b/codex-rs/exec/src/cli.rs @@ -71,6 +71,10 @@ pub struct Cli { #[arg(long = "add-dir", value_name = "DIR", value_hint = clap::ValueHint::DirPath)] pub add_dir: Vec, + /// Run without persisting session files to disk. + #[arg(long = "ephemeral", global = true, default_value_t = false)] + pub ephemeral: bool, + /// Path to a JSON Schema file describing the model's final response shape. #[arg(long = "output-schema", value_name = "FILE")] pub output_schema: Option, @@ -262,9 +266,11 @@ mod tests { "gpt-5.2-codex", "--dangerously-bypass-approvals-and-sandbox", "--skip-git-repo-check", + "--ephemeral", PROMPT, ]); + assert!(cli.ephemeral); let Some(Command::Resume(args)) = cli.command else { panic!("expected resume command"); }; diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs index 9175b100a5..cbe45b92f1 100644 --- a/codex-rs/exec/src/event_processor_with_human_output.rs +++ b/codex-rs/exec/src/event_processor_with_human_output.rs @@ -593,17 +593,20 @@ impl EventProcessor for EventProcessorWithHumanOutput { view.path.display() ); } - EventMsg::TurnAborted(abort_reason) => match abort_reason.reason { - TurnAbortReason::Interrupted => { - ts_msg!(self, "task interrupted"); + EventMsg::TurnAborted(abort_reason) => { + match abort_reason.reason { + TurnAbortReason::Interrupted => { + ts_msg!(self, "task interrupted"); + } + TurnAbortReason::Replaced => { + ts_msg!(self, "task aborted: replaced by a new task"); + } + TurnAbortReason::ReviewEnded => { + ts_msg!(self, "task aborted: review ended"); + } } - TurnAbortReason::Replaced => { - ts_msg!(self, "task aborted: replaced by a new task"); - } - TurnAbortReason::ReviewEnded => { - ts_msg!(self, "task aborted: review ended"); - } - }, + return CodexStatus::InitiateShutdown; + } EventMsg::ContextCompacted(_) => { ts_msg!(self, "context compacted"); } diff --git a/codex-rs/exec/src/event_processor_with_jsonl_output.rs b/codex-rs/exec/src/event_processor_with_jsonl_output.rs index 823fa7b810..9675651ef7 100644 --- a/codex-rs/exec/src/event_processor_with_jsonl_output.rs +++ b/codex-rs/exec/src/event_processor_with_jsonl_output.rs @@ -871,6 +871,7 @@ impl EventProcessor for EventProcessorWithJsonOutput { } CodexStatus::InitiateShutdown } + protocol::EventMsg::TurnAborted(_) => CodexStatus::InitiateShutdown, protocol::EventMsg::ShutdownComplete => CodexStatus::Shutdown, _ => CodexStatus::Running, } diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index a6ded3927a..0f8e8ad72e 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -103,6 +103,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option) -> any cwd, skip_git_repo_check, add_dir, + ephemeral, color, last_message_file, json: json_mode, @@ -254,7 +255,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option) -> any include_apply_patch_tool: None, show_raw_agent_reasoning: oss.then_some(true), tools_web_search_request: None, - ephemeral: None, + ephemeral: ephemeral.then_some(true), additional_writable_roots: add_dir, }; diff --git a/codex-rs/exec/tests/suite/ephemeral.rs b/codex-rs/exec/tests/suite/ephemeral.rs new file mode 100644 index 0000000000..533a732f94 --- /dev/null +++ b/codex-rs/exec/tests/suite/ephemeral.rs @@ -0,0 +1,55 @@ +#![cfg(not(target_os = "windows"))] +#![allow(clippy::expect_used, clippy::unwrap_used)] + +use codex_utils_cargo_bin::find_resource; +use core_test_support::test_codex_exec::test_codex_exec; +use walkdir::WalkDir; + +fn session_rollout_count(home_path: &std::path::Path) -> usize { + let sessions_dir = home_path.join("sessions"); + if !sessions_dir.exists() { + return 0; + } + + WalkDir::new(sessions_dir) + .into_iter() + .filter_map(Result::ok) + .filter(|entry| entry.file_type().is_file()) + .filter(|entry| entry.file_name().to_string_lossy().ends_with(".jsonl")) + .count() +} + +#[test] +fn persists_rollout_file_by_default() -> anyhow::Result<()> { + let test = test_codex_exec(); + let fixture = find_resource!("tests/fixtures/cli_responses_fixture.sse")?; + + test.cmd() + .env("CODEX_RS_SSE_FIXTURE", &fixture) + .env("OPENAI_BASE_URL", "http://unused.local") + .arg("--skip-git-repo-check") + .arg("default persistence behavior") + .assert() + .code(0); + + assert_eq!(session_rollout_count(test.home_path()), 1); + Ok(()) +} + +#[test] +fn does_not_persist_rollout_file_in_ephemeral_mode() -> anyhow::Result<()> { + let test = test_codex_exec(); + let fixture = find_resource!("tests/fixtures/cli_responses_fixture.sse")?; + + test.cmd() + .env("CODEX_RS_SSE_FIXTURE", &fixture) + .env("OPENAI_BASE_URL", "http://unused.local") + .arg("--skip-git-repo-check") + .arg("--ephemeral") + .arg("ephemeral behavior") + .assert() + .code(0); + + assert_eq!(session_rollout_count(test.home_path()), 0); + Ok(()) +} diff --git a/codex-rs/exec/tests/suite/mod.rs b/codex-rs/exec/tests/suite/mod.rs index 77012ee3b7..dbfc966df4 100644 --- a/codex-rs/exec/tests/suite/mod.rs +++ b/codex-rs/exec/tests/suite/mod.rs @@ -2,6 +2,7 @@ mod add_dir; mod apply_patch; mod auth_env; +mod ephemeral; mod originator; mod output_schema; mod resume; diff --git a/codex-rs/exec/tests/suite/sandbox.rs b/codex-rs/exec/tests/suite/sandbox.rs index 303023b158..ab8d3868d9 100644 --- a/codex-rs/exec/tests/suite/sandbox.rs +++ b/codex-rs/exec/tests/suite/sandbox.rs @@ -50,15 +50,76 @@ async fn spawn_command_under_sandbox( command_cwd, sandbox_policy, sandbox_cwd, + false, stdio_policy, env, ) .await } +#[cfg(target_os = "linux")] +/// Determines whether Linux sandbox tests can run on this host. +/// +/// These tests require an enforceable filesystem sandbox. We run a tiny command +/// under the production Landlock path and skip when enforcement is unavailable +/// (for example on kernels or container profiles where Landlock is not +/// enforced). +async fn linux_sandbox_test_env() -> Option> { + let command_cwd = std::env::current_dir().ok()?; + let sandbox_cwd = command_cwd.clone(); + let policy = SandboxPolicy::ReadOnly; + + if can_apply_linux_sandbox_policy(&policy, &command_cwd, sandbox_cwd.as_path(), HashMap::new()) + .await + { + return Some(HashMap::new()); + } + + eprintln!("Skipping test: Landlock is not enforceable on this host."); + None +} + +#[cfg(target_os = "linux")] +/// Returns whether a minimal command can run successfully with the requested +/// Linux sandbox policy applied. +/// +/// This is used as a capability probe so sandbox behavior tests only run when +/// Landlock enforcement is actually active. +async fn can_apply_linux_sandbox_policy( + policy: &SandboxPolicy, + command_cwd: &Path, + sandbox_cwd: &Path, + env: HashMap, +) -> bool { + let spawn_result = spawn_command_under_sandbox( + vec!["/usr/bin/true".to_string()], + command_cwd.to_path_buf(), + policy, + sandbox_cwd, + StdioPolicy::RedirectForShellTool, + env, + ) + .await; + let Ok(mut child) = spawn_result else { + return false; + }; + child + .wait() + .await + .map(|status| status.success()) + .unwrap_or(false) +} + #[tokio::test] async fn python_multiprocessing_lock_works_under_sandbox() { core_test_support::skip_if_sandbox!(); + #[cfg(target_os = "linux")] + let sandbox_env = match linux_sandbox_test_env().await { + Some(env) => env, + None => return, + }; + #[cfg(not(target_os = "linux"))] + let sandbox_env = HashMap::new(); #[cfg(target_os = "macos")] let writable_roots = Vec::::new(); @@ -102,7 +163,7 @@ if __name__ == '__main__': &policy, sandbox_cwd.as_path(), StdioPolicy::Inherit, - HashMap::new(), + sandbox_env, ) .await .expect("should be able to spawn python under sandbox"); @@ -114,6 +175,13 @@ if __name__ == '__main__': #[tokio::test] async fn python_getpwuid_works_under_sandbox() { core_test_support::skip_if_sandbox!(); + #[cfg(target_os = "linux")] + let sandbox_env = match linux_sandbox_test_env().await { + Some(env) => env, + None => return, + }; + #[cfg(not(target_os = "linux"))] + let sandbox_env = HashMap::new(); if std::process::Command::new("python3") .arg("--version") @@ -138,7 +206,7 @@ async fn python_getpwuid_works_under_sandbox() { &policy, sandbox_cwd.as_path(), StdioPolicy::RedirectForShellTool, - HashMap::new(), + sandbox_env, ) .await .expect("should be able to spawn python under sandbox"); @@ -153,6 +221,13 @@ async fn python_getpwuid_works_under_sandbox() { #[tokio::test] async fn sandbox_distinguishes_command_and_policy_cwds() { core_test_support::skip_if_sandbox!(); + #[cfg(target_os = "linux")] + let sandbox_env = match linux_sandbox_test_env().await { + Some(env) => env, + None => return, + }; + #[cfg(not(target_os = "linux"))] + let sandbox_env = HashMap::new(); let temp = tempfile::tempdir().expect("should be able to create temp dir"); let sandbox_root = temp.path().join("sandbox"); let command_root = temp.path().join("command"); @@ -186,7 +261,7 @@ async fn sandbox_distinguishes_command_and_policy_cwds() { &policy, canonical_sandbox_root.as_path(), StdioPolicy::Inherit, - HashMap::new(), + sandbox_env.clone(), ) .await .expect("should spawn command writing to forbidden path"); @@ -217,7 +292,7 @@ async fn sandbox_distinguishes_command_and_policy_cwds() { &policy, canonical_sandbox_root.as_path(), StdioPolicy::Inherit, - HashMap::new(), + sandbox_env, ) .await .expect("should spawn command writing to sandbox root"); diff --git a/codex-rs/linux-sandbox/Cargo.toml b/codex-rs/linux-sandbox/Cargo.toml index 3feb1eb7b8..ff16a569fe 100644 --- a/codex-rs/linux-sandbox/Cargo.toml +++ b/codex-rs/linux-sandbox/Cargo.toml @@ -23,7 +23,6 @@ landlock = { workspace = true } libc = { workspace = true } seccompiler = { workspace = true } serde_json = { workspace = true } -which = "8.0.0" [target.'cfg(target_os = "linux")'.dev-dependencies] pretty_assertions = { workspace = true } diff --git a/codex-rs/linux-sandbox/README.md b/codex-rs/linux-sandbox/README.md index 676f234954..f14fc5f13d 100644 --- a/codex-rs/linux-sandbox/README.md +++ b/codex-rs/linux-sandbox/README.md @@ -6,3 +6,28 @@ This crate is responsible for producing: - a lib crate that exposes the business logic of the executable as `run_main()` so that - the `codex-exec` CLI can check if its arg0 is `codex-linux-sandbox` and, if so, execute as if it were `codex-linux-sandbox` - this should also be true of the `codex` multitool CLI + +On Linux, the bubblewrap pipeline uses the vendored bubblewrap path compiled +into this binary. + +**Current Behavior** +- Legacy Landlock + mount protections remain available as the legacy pipeline. +- The bubblewrap pipeline is standardized on the vendored path. +- During rollout, the bubblewrap pipeline is gated by the temporary feature + flag `use_linux_sandbox_bwrap` (CLI `-c` alias for + `features.use_linux_sandbox_bwrap`; legacy remains default when off). +- When enabled, the bubblewrap pipeline applies `PR_SET_NO_NEW_PRIVS` and a + seccomp network filter in-process. +- When enabled, the filesystem is read-only by default via `--ro-bind / /`. +- When enabled, writable roots are layered with `--bind `. +- When enabled, protected subpaths under writable roots (for example `.git`, + resolved `gitdir:`, and `.codex`) are re-applied as read-only via `--ro-bind`. +- When enabled, symlink-in-path and non-existent protected paths inside + writable roots are blocked by mounting `/dev/null` on the symlink or first + missing component. +- When enabled, the helper isolates the PID namespace via `--unshare-pid`. +- When enabled, it mounts a fresh `/proc` via `--proc /proc` by default, but + you can skip this in restrictive container environments with `--no-proc`. + +**Notes** +- The CLI surface still uses legacy names like `codex debug landlock`. diff --git a/codex-rs/linux-sandbox/src/bwrap.rs b/codex-rs/linux-sandbox/src/bwrap.rs index c1e0732e08..4a60c65dd3 100644 --- a/codex-rs/linux-sandbox/src/bwrap.rs +++ b/codex-rs/linux-sandbox/src/bwrap.rs @@ -44,45 +44,6 @@ pub(crate) fn create_bwrap_command_args( sandbox_policy: &SandboxPolicy, cwd: &Path, options: BwrapOptions, - bwrap_path: Option<&Path>, -) -> Result> { - if sandbox_policy.has_full_disk_write_access() { - return Ok(command); - } - - let bwrap_path = match bwrap_path { - Some(path) => { - if path.exists() { - path.to_path_buf() - } else { - return Err(CodexErr::UnsupportedOperation(format!( - "bubblewrap (bwrap) not found at configured path: {}", - path.display() - ))); - } - } - None => which::which("bwrap").map_err(|err| { - CodexErr::UnsupportedOperation(format!("bubblewrap (bwrap) not found on PATH: {err}")) - })?, - }; - - let mut args = Vec::new(); - args.push(path_to_string(&bwrap_path)); - args.extend(create_bwrap_flags(command, sandbox_policy, cwd, options)?); - Ok(args) -} - -/// Doc-hidden helper that builds bubblewrap arguments without a program path. -/// -/// This is intended for experiments where we call a build-time bubblewrap -/// `main` symbol via FFI rather than exec'ing the `bwrap` binary. The caller -/// is responsible for providing a suitable `argv[0]`. -#[doc(hidden)] -pub(crate) fn create_bwrap_command_args_vendored( - command: Vec, - sandbox_policy: &SandboxPolicy, - cwd: &Path, - options: BwrapOptions, ) -> Result> { if sandbox_policy.has_full_disk_write_access() { return Ok(command); diff --git a/codex-rs/linux-sandbox/src/landlock.rs b/codex-rs/linux-sandbox/src/landlock.rs index 772176fc90..4853011792 100644 --- a/codex-rs/linux-sandbox/src/landlock.rs +++ b/codex-rs/linux-sandbox/src/landlock.rs @@ -1,3 +1,7 @@ +//! In-process Linux sandbox primitives: `no_new_privs` and seccomp. +//! +//! Filesystem restrictions are enforced by bubblewrap in `linux_run_main`. +//! Landlock helpers remain available here as legacy/backup utilities. use std::collections::BTreeMap; use std::path::Path; @@ -8,6 +12,7 @@ use codex_core::protocol::SandboxPolicy; use codex_utils_absolute_path::AbsolutePathBuf; use landlock::ABI; +#[allow(unused_imports)] use landlock::Access; use landlock::AccessFs; use landlock::CompatLevel; @@ -27,11 +32,24 @@ use seccompiler::apply_filter; /// Apply sandbox policies inside this thread so only the child inherits /// them, not the entire CLI process. +/// +/// This function is responsible for: +/// - enabling `PR_SET_NO_NEW_PRIVS` when restrictions apply, and +/// - installing the network seccomp filter when network access is disabled. +/// +/// Filesystem restrictions are intentionally handled by bubblewrap. pub(crate) fn apply_sandbox_policy_to_current_thread( sandbox_policy: &SandboxPolicy, cwd: &Path, + apply_landlock_fs: bool, ) -> Result<()> { - if !sandbox_policy.has_full_disk_write_access() || !sandbox_policy.has_full_network_access() { + // `PR_SET_NO_NEW_PRIVS` is required for seccomp, but it also prevents + // setuid privilege elevation. Many `bwrap` deployments rely on setuid, so + // we avoid this unless we need seccomp or we are explicitly using the + // legacy Landlock filesystem pipeline. + if !sandbox_policy.has_full_network_access() + || (apply_landlock_fs && !sandbox_policy.has_full_disk_write_access()) + { set_no_new_privs()?; } @@ -39,7 +57,7 @@ pub(crate) fn apply_sandbox_policy_to_current_thread( install_network_seccomp_filter_on_current_thread()?; } - if !sandbox_policy.has_full_disk_write_access() { + if apply_landlock_fs && !sandbox_policy.has_full_disk_write_access() { let writable_roots = sandbox_policy .get_writable_roots_with_cwd(cwd) .into_iter() @@ -54,6 +72,7 @@ pub(crate) fn apply_sandbox_policy_to_current_thread( Ok(()) } +/// Enable `PR_SET_NO_NEW_PRIVS` so seccomp can be applied safely. fn set_no_new_privs() -> Result<()> { let result = unsafe { libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) }; if result != 0 { @@ -68,6 +87,9 @@ fn set_no_new_privs() -> Result<()> { /// /// # Errors /// Returns [`CodexErr::Sandbox`] variants when the ruleset fails to apply. +/// +/// Note: this is currently unused because filesystem sandboxing is performed +/// via bubblewrap. It is kept for reference and potential fallback use. fn install_filesystem_landlock_rules_on_current_thread( writable_roots: Vec, ) -> Result<()> { @@ -98,6 +120,9 @@ fn install_filesystem_landlock_rules_on_current_thread( /// Installs a seccomp filter that blocks outbound network access except for /// AF_UNIX domain sockets. +/// +/// The filter is applied to the current thread so only the sandboxed child +/// inherits it. fn install_network_seccomp_filter_on_current_thread() -> std::result::Result<(), SandboxErr> { // Build rule map. let mut rules: BTreeMap> = BTreeMap::new(); diff --git a/codex-rs/linux-sandbox/src/linux_run_main.rs b/codex-rs/linux-sandbox/src/linux_run_main.rs index 3f6cd3ac86..f5f0d9887a 100644 --- a/codex-rs/linux-sandbox/src/linux_run_main.rs +++ b/codex-rs/linux-sandbox/src/linux_run_main.rs @@ -1,13 +1,16 @@ use clap::Parser; use std::ffi::CString; +use std::fs::File; +use std::io::Read; +use std::os::fd::FromRawFd; use std::path::Path; use std::path::PathBuf; use crate::bwrap::BwrapOptions; use crate::bwrap::create_bwrap_command_args; -use crate::bwrap::create_bwrap_command_args_vendored; use crate::landlock::apply_sandbox_policy_to_current_thread; use crate::vendored_bwrap::exec_vendored_bwrap; +use crate::vendored_bwrap::run_vendored_bwrap_main; #[derive(Debug, Parser)] /// CLI surface for the Linux sandbox helper. @@ -29,18 +32,6 @@ pub struct LandlockCommand { #[arg(long = "use-bwrap-sandbox", hide = true, default_value_t = false)] pub use_bwrap_sandbox: bool, - /// Optional explicit path to the `bwrap` binary to use. - /// - /// When provided, this implies bubblewrap opt-in and avoids PATH lookups. - #[arg(long = "bwrap-path", hide = true)] - pub bwrap_path: Option, - - /// Experimental: call a build-time bubblewrap `main()` via FFI. - /// - /// This is opt-in and only works when the build script compiles bwrap. - #[arg(long = "use-vendored-bwrap", hide = true, default_value_t = false)] - pub use_vendored_bwrap: bool, - /// Internal: apply seccomp and `no_new_privs` in the already-sandboxed /// process, then exec the user command. /// @@ -72,13 +63,10 @@ pub fn run_main() -> ! { sandbox_policy_cwd, sandbox_policy, use_bwrap_sandbox, - bwrap_path, - use_vendored_bwrap, apply_seccomp_then_exec, no_proc, command, } = LandlockCommand::parse(); - let use_bwrap_sandbox = use_bwrap_sandbox || bwrap_path.is_some() || use_vendored_bwrap; if command.is_empty() { panic!("No command specified to execute."); @@ -87,82 +75,199 @@ pub fn run_main() -> ! { // Inner stage: apply seccomp/no_new_privs after bubblewrap has already // established the filesystem view. if apply_seccomp_then_exec { - if let Err(e) = apply_sandbox_policy_to_current_thread(&sandbox_policy, &sandbox_policy_cwd) + if let Err(e) = + apply_sandbox_policy_to_current_thread(&sandbox_policy, &sandbox_policy_cwd, false) { panic!("error applying Linux sandbox restrictions: {e:?}"); } exec_or_panic(command); } - let command = if sandbox_policy.has_full_disk_write_access() { - if let Err(e) = apply_sandbox_policy_to_current_thread(&sandbox_policy, &sandbox_policy_cwd) + if sandbox_policy.has_full_disk_write_access() { + if let Err(e) = + apply_sandbox_policy_to_current_thread(&sandbox_policy, &sandbox_policy_cwd, false) { panic!("error applying Linux sandbox restrictions: {e:?}"); } - command - } else if use_bwrap_sandbox { + exec_or_panic(command); + } + + if use_bwrap_sandbox { // Outer stage: bubblewrap first, then re-enter this binary in the - // sandboxed environment to apply seccomp. + // sandboxed environment to apply seccomp. This path never falls back + // to legacy Landlock on failure. let inner = build_inner_seccomp_command( &sandbox_policy_cwd, &sandbox_policy, use_bwrap_sandbox, - bwrap_path.as_deref(), command, ); - let options = BwrapOptions { - mount_proc: !no_proc, - }; - if use_vendored_bwrap { - let mut argv0 = bwrap_path - .as_deref() - .map(|path| path.to_string_lossy().to_string()) - .unwrap_or_else(|| "bwrap".to_string()); - if argv0.is_empty() { - argv0 = "bwrap".to_string(); - } - - let mut argv = vec![argv0]; - argv.extend( - create_bwrap_command_args_vendored( - inner, - &sandbox_policy, - &sandbox_policy_cwd, - options, - ) - .unwrap_or_else(|err| { - panic!("error building build-time bubblewrap command: {err:?}") - }), - ); - exec_vendored_bwrap(argv); - } - ensure_bwrap_available(bwrap_path.as_deref()); - create_bwrap_command_args( - inner, - &sandbox_policy, - &sandbox_policy_cwd, - options, - bwrap_path.as_deref(), - ) - .unwrap_or_else(|err| panic!("error building bubblewrap command: {err:?}")) - } else { - // Legacy path: Landlock enforcement only. - if let Err(e) = apply_sandbox_policy_to_current_thread(&sandbox_policy, &sandbox_policy_cwd) - { - panic!("error applying legacy Linux sandbox restrictions: {e:?}"); - } - command - }; + run_bwrap_with_proc_fallback(&sandbox_policy_cwd, &sandbox_policy, inner, !no_proc); + } + // Legacy path: Landlock enforcement only, when bwrap sandboxing is not enabled. + if let Err(e) = + apply_sandbox_policy_to_current_thread(&sandbox_policy, &sandbox_policy_cwd, true) + { + panic!("error applying legacy Linux sandbox restrictions: {e:?}"); + } exec_or_panic(command); } +fn run_bwrap_with_proc_fallback( + sandbox_policy_cwd: &Path, + sandbox_policy: &codex_core::protocol::SandboxPolicy, + inner: Vec, + mount_proc: bool, +) -> ! { + let mut mount_proc = mount_proc; + + if mount_proc && !preflight_proc_mount_support(sandbox_policy_cwd, sandbox_policy) { + eprintln!("codex-linux-sandbox: bwrap could not mount /proc; retrying with --no-proc"); + mount_proc = false; + } + + let options = BwrapOptions { mount_proc }; + let argv = build_bwrap_argv(inner, sandbox_policy, sandbox_policy_cwd, options); + exec_vendored_bwrap(argv); +} + +fn build_bwrap_argv( + inner: Vec, + sandbox_policy: &codex_core::protocol::SandboxPolicy, + sandbox_policy_cwd: &Path, + options: BwrapOptions, +) -> Vec { + let mut args = create_bwrap_command_args(inner, sandbox_policy, sandbox_policy_cwd, options) + .unwrap_or_else(|err| panic!("error building bubblewrap command: {err:?}")); + + let command_separator_index = args + .iter() + .position(|arg| arg == "--") + .unwrap_or_else(|| panic!("bubblewrap argv is missing command separator '--'")); + args.splice( + command_separator_index..command_separator_index, + ["--argv0".to_string(), "codex-linux-sandbox".to_string()], + ); + + let mut argv = vec!["bwrap".to_string()]; + argv.extend(args); + argv +} + +fn preflight_proc_mount_support( + sandbox_policy_cwd: &Path, + sandbox_policy: &codex_core::protocol::SandboxPolicy, +) -> bool { + let preflight_command = vec![resolve_true_command()]; + let preflight_argv = build_bwrap_argv( + preflight_command, + sandbox_policy, + sandbox_policy_cwd, + BwrapOptions { mount_proc: true }, + ); + let stderr = run_bwrap_in_child_capture_stderr(preflight_argv); + !is_proc_mount_failure(stderr.as_str()) +} + +fn resolve_true_command() -> String { + for candidate in ["/usr/bin/true", "/bin/true"] { + if Path::new(candidate).exists() { + return candidate.to_string(); + } + } + "true".to_string() +} + +/// Run a short-lived bubblewrap preflight in a child process and capture stderr. +/// +/// Strategy: +/// - This is used only by `preflight_proc_mount_support`, which runs `/bin/true` +/// under bubblewrap with `--proc /proc`. +/// - The goal is to detect environments where mounting `/proc` fails (for +/// example, restricted containers), so we can retry the real run with +/// `--no-proc`. +/// - We capture stderr from that preflight to match known mount-failure text. +/// We do not stream it because this is a one-shot probe with a trivial +/// command, and reads are bounded to a fixed max size. +fn run_bwrap_in_child_capture_stderr(argv: Vec) -> String { + const MAX_PREFLIGHT_STDERR_BYTES: u64 = 64 * 1024; + + let mut pipe_fds = [0; 2]; + let pipe_res = unsafe { libc::pipe2(pipe_fds.as_mut_ptr(), libc::O_CLOEXEC) }; + if pipe_res < 0 { + let err = std::io::Error::last_os_error(); + panic!("failed to create stderr pipe for bubblewrap: {err}"); + } + let read_fd = pipe_fds[0]; + let write_fd = pipe_fds[1]; + + let pid = unsafe { libc::fork() }; + if pid < 0 { + let err = std::io::Error::last_os_error(); + panic!("failed to fork for bubblewrap: {err}"); + } + + if pid == 0 { + // Child: redirect stderr to the pipe, then run bubblewrap. + unsafe { + close_fd_or_panic(read_fd, "close read end in bubblewrap child"); + if libc::dup2(write_fd, libc::STDERR_FILENO) < 0 { + let err = std::io::Error::last_os_error(); + panic!("failed to redirect stderr for bubblewrap: {err}"); + } + close_fd_or_panic(write_fd, "close write end in bubblewrap child"); + } + + let exit_code = run_vendored_bwrap_main(&argv); + std::process::exit(exit_code); + } + + // Parent: close the write end and read stderr while the child runs. + close_fd_or_panic(write_fd, "close write end in bubblewrap parent"); + + // SAFETY: `read_fd` is a valid owned fd in the parent. + let mut read_file = unsafe { File::from_raw_fd(read_fd) }; + let mut stderr_bytes = Vec::new(); + let mut limited_reader = (&mut read_file).take(MAX_PREFLIGHT_STDERR_BYTES); + if let Err(err) = limited_reader.read_to_end(&mut stderr_bytes) { + panic!("failed to read bubblewrap stderr: {err}"); + } + + let mut status: libc::c_int = 0; + let wait_res = unsafe { libc::waitpid(pid, &mut status as *mut libc::c_int, 0) }; + if wait_res < 0 { + let err = std::io::Error::last_os_error(); + panic!("waitpid failed for bubblewrap child: {err}"); + } + + String::from_utf8_lossy(&stderr_bytes).into_owned() +} + +/// Close an owned file descriptor and panic with context on failure. +/// +/// We use explicit close() checks here (instead of ignoring return codes) +/// because this code runs in low-level sandbox setup paths where fd leaks or +/// close errors can mask the root cause of later failures. +fn close_fd_or_panic(fd: libc::c_int, context: &str) { + let close_res = unsafe { libc::close(fd) }; + if close_res < 0 { + let err = std::io::Error::last_os_error(); + panic!("{context}: {err}"); + } +} + +fn is_proc_mount_failure(stderr: &str) -> bool { + stderr.contains("Can't mount proc") + && stderr.contains("/newroot/proc") + && stderr.contains("Invalid argument") +} + /// Build the inner command that applies seccomp after bubblewrap. fn build_inner_seccomp_command( sandbox_policy_cwd: &Path, sandbox_policy: &codex_core::protocol::SandboxPolicy, use_bwrap_sandbox: bool, - bwrap_path: Option<&Path>, command: Vec, ) -> Vec { let current_exe = match std::env::current_exe() { @@ -185,10 +290,6 @@ fn build_inner_seccomp_command( inner.push("--use-bwrap-sandbox".to_string()); inner.push("--apply-seccomp-then-exec".to_string()); } - if let Some(bwrap_path) = bwrap_path { - inner.push("--bwrap-path".to_string()); - inner.push(bwrap_path.to_string_lossy().to_string()); - } inner.push("--".to_string()); inner.extend(command); inner @@ -217,32 +318,52 @@ fn exec_or_panic(command: Vec) -> ! { panic!("Failed to execvp {}: {err}", command[0].as_str()); } -/// Ensure the `bwrap` binary is available when the sandbox needs it. -fn ensure_bwrap_available(bwrap_path: Option<&Path>) { - if let Some(path) = bwrap_path { - if path.exists() { - return; - } - panic!( - "bubblewrap (bwrap) is required for Linux filesystem sandboxing but was not found at the configured path: {}\n\ -Install it and retry. Examples:\n\ -- Debian/Ubuntu: apt-get install bubblewrap\n\ -- Fedora/RHEL: dnf install bubblewrap\n\ -- Arch: pacman -S bubblewrap\n\ -If you are running the Codex Node package, ensure bwrap is installed on the host system.", - path.display() - ); - } - if which::which("bwrap").is_ok() { - return; +#[cfg(test)] +mod tests { + use super::*; + use codex_core::protocol::SandboxPolicy; + use pretty_assertions::assert_eq; + + #[test] + fn detects_proc_mount_invalid_argument_failure() { + let stderr = "bwrap: Can't mount proc on /newroot/proc: Invalid argument"; + assert_eq!(is_proc_mount_failure(stderr), true); } - panic!( - "bubblewrap (bwrap) is required for Linux filesystem sandboxing but was not found on PATH.\n\ -Install it and retry. Examples:\n\ -- Debian/Ubuntu: apt-get install bubblewrap\n\ -- Fedora/RHEL: dnf install bubblewrap\n\ -- Arch: pacman -S bubblewrap\n\ -If you are running the Codex Node package, ensure bwrap is installed on the host system." - ); + #[test] + fn ignores_non_proc_mount_errors() { + let stderr = "bwrap: Can't bind mount /dev/null: Operation not permitted"; + assert_eq!(is_proc_mount_failure(stderr), false); + } + + #[test] + fn inserts_bwrap_argv0_before_command_separator() { + let argv = build_bwrap_argv( + vec!["/bin/true".to_string()], + &SandboxPolicy::ReadOnly, + Path::new("/"), + BwrapOptions { mount_proc: true }, + ); + assert_eq!( + argv, + vec![ + "bwrap".to_string(), + "--new-session".to_string(), + "--die-with-parent".to_string(), + "--ro-bind".to_string(), + "/".to_string(), + "/".to_string(), + "--dev-bind".to_string(), + "/dev/null".to_string(), + "/dev/null".to_string(), + "--unshare-pid".to_string(), + "--proc".to_string(), + "/proc".to_string(), + "--argv0".to_string(), + "codex-linux-sandbox".to_string(), + "--".to_string(), + "/bin/true".to_string(), + ] + ); + } } diff --git a/codex-rs/linux-sandbox/src/mounts.rs b/codex-rs/linux-sandbox/src/mounts.rs deleted file mode 100644 index 04f3651dff..0000000000 --- a/codex-rs/linux-sandbox/src/mounts.rs +++ /dev/null @@ -1,339 +0,0 @@ -#![allow(dead_code)] - -use std::ffi::CString; -use std::os::unix::ffi::OsStrExt; -use std::path::Path; - -use codex_core::error::CodexErr; -use codex_core::error::Result; -use codex_core::protocol::SandboxPolicy; -use codex_core::protocol::WritableRoot; -use codex_utils_absolute_path::AbsolutePathBuf; - -/// Apply read-only bind mounts for protected subpaths before Landlock. -/// -/// This unshares mount namespaces (and user namespaces for non-root) so the -/// read-only remounts do not affect the host, then bind-mounts each protected -/// target onto itself and remounts it read-only. -pub(crate) fn apply_read_only_mounts(sandbox_policy: &SandboxPolicy, cwd: &Path) -> Result<()> { - let writable_roots = sandbox_policy.get_writable_roots_with_cwd(cwd); - let mount_targets = collect_read_only_mount_targets(&writable_roots)?; - if mount_targets.is_empty() { - return Ok(()); - } - - // Root can unshare the mount namespace directly; non-root needs a user - // namespace to gain capabilities for remounting. - if is_running_as_root() { - unshare_mount_namespace()?; - } else { - let original_euid = unsafe { libc::geteuid() }; - let original_egid = unsafe { libc::getegid() }; - unshare_user_and_mount_namespaces()?; - write_user_namespace_maps(original_euid, original_egid)?; - } - make_mounts_private()?; - - for target in mount_targets { - // Bind and remount read-only works for both files and directories. - bind_mount_read_only(target.as_path())?; - } - - // Drop ambient capabilities acquired from the user namespace so the - // sandboxed command cannot remount or create new bind mounts. - if !is_running_as_root() { - drop_caps()?; - } - - Ok(()) -} - -/// Collect read-only mount targets, resolving worktree `.git` pointer files. -fn collect_read_only_mount_targets( - writable_roots: &[WritableRoot], -) -> Result> { - let mut targets = Vec::new(); - for writable_root in writable_roots { - for ro_subpath in &writable_root.read_only_subpaths { - // The policy expects these paths to exist; surface actionable errors - // rather than silently skipping protections. - if !ro_subpath.as_path().exists() { - return Err(CodexErr::UnsupportedOperation(format!( - "Sandbox expected to protect {path}, but it does not exist. Ensure the repository contains this path or create it before running Codex.", - path = ro_subpath.as_path().display() - ))); - } - targets.push(ro_subpath.clone()); - // Worktrees and submodules store `.git` as a pointer file; add the - // referenced gitdir as an extra read-only target. - if is_git_pointer_file(ro_subpath) { - let gitdir = resolve_gitdir_from_file(ro_subpath)?; - if !targets - .iter() - .any(|target| target.as_path() == gitdir.as_path()) - { - targets.push(gitdir); - } - } - } - } - Ok(targets) -} - -/// Detect a `.git` pointer file used by worktrees and submodules. -fn is_git_pointer_file(path: &AbsolutePathBuf) -> bool { - path.as_path().is_file() && path.as_path().file_name() == Some(std::ffi::OsStr::new(".git")) -} - -/// Resolve a worktree `.git` pointer file to its gitdir path. -fn resolve_gitdir_from_file(dot_git: &AbsolutePathBuf) -> Result { - let contents = std::fs::read_to_string(dot_git.as_path()).map_err(CodexErr::from)?; - let trimmed = contents.trim(); - let (_, gitdir_raw) = trimmed.split_once(':').ok_or_else(|| { - CodexErr::UnsupportedOperation(format!( - "Expected {path} to contain a gitdir pointer, but it did not match `gitdir: `.", - path = dot_git.as_path().display() - )) - })?; - // `gitdir: ` may be relative to the directory containing `.git`. - let gitdir_raw = gitdir_raw.trim(); - if gitdir_raw.is_empty() { - return Err(CodexErr::UnsupportedOperation(format!( - "Expected {path} to contain a gitdir pointer, but it was empty.", - path = dot_git.as_path().display() - ))); - } - let base = dot_git.as_path().parent().ok_or_else(|| { - CodexErr::UnsupportedOperation(format!( - "Unable to resolve parent directory for {path}.", - path = dot_git.as_path().display() - )) - })?; - let gitdir_path = AbsolutePathBuf::resolve_path_against_base(gitdir_raw, base)?; - if !gitdir_path.as_path().exists() { - return Err(CodexErr::UnsupportedOperation(format!( - "Resolved gitdir path {path} does not exist.", - path = gitdir_path.as_path().display() - ))); - } - Ok(gitdir_path) -} - -/// Unshare the mount namespace so mount changes are isolated to the sandboxed process. -fn unshare_mount_namespace() -> Result<()> { - let result = unsafe { libc::unshare(libc::CLONE_NEWNS) }; - if result != 0 { - return Err(std::io::Error::last_os_error().into()); - } - Ok(()) -} - -/// Unshare user + mount namespaces so the process can remount read-only without privileges. -fn unshare_user_and_mount_namespaces() -> Result<()> { - let result = unsafe { libc::unshare(libc::CLONE_NEWUSER | libc::CLONE_NEWNS) }; - if result != 0 { - return Err(std::io::Error::last_os_error().into()); - } - Ok(()) -} - -fn is_running_as_root() -> bool { - unsafe { libc::geteuid() == 0 } -} - -#[repr(C)] -struct CapUserHeader { - version: u32, - pid: i32, -} - -#[repr(C)] -struct CapUserData { - effective: u32, - permitted: u32, - inheritable: u32, -} - -const LINUX_CAPABILITY_VERSION_3: u32 = 0x2008_0522; - -/// Map the provided uid/gid to root inside the user namespace. -fn write_user_namespace_maps(uid: libc::uid_t, gid: libc::gid_t) -> Result<()> { - write_proc_file("/proc/self/setgroups", "deny\n")?; - - write_proc_file("/proc/self/uid_map", format!("0 {uid} 1\n"))?; - write_proc_file("/proc/self/gid_map", format!("0 {gid} 1\n"))?; - Ok(()) -} - -/// Drop all capabilities in the current user namespace. -fn drop_caps() -> Result<()> { - let mut header = CapUserHeader { - version: LINUX_CAPABILITY_VERSION_3, - pid: 0, - }; - let data = [ - CapUserData { - effective: 0, - permitted: 0, - inheritable: 0, - }, - CapUserData { - effective: 0, - permitted: 0, - inheritable: 0, - }, - ]; - - // Use syscall directly to avoid libc capability symbols that are missing on musl. - let result = unsafe { libc::syscall(libc::SYS_capset, &mut header, data.as_ptr()) }; - if result != 0 { - return Err(std::io::Error::last_os_error().into()); - } - Ok(()) -} - -/// Write a small procfs file, returning a sandbox error on failure. -fn write_proc_file(path: &str, contents: impl AsRef<[u8]>) -> Result<()> { - std::fs::write(path, contents)?; - Ok(()) -} - -/// Ensure mounts are private so remounting does not propagate outside the namespace. -fn make_mounts_private() -> Result<()> { - let root = CString::new("/").map_err(|_| { - CodexErr::UnsupportedOperation("Sandbox mount path contains NUL byte: /".to_string()) - })?; - let result = unsafe { - libc::mount( - std::ptr::null(), - root.as_ptr(), - std::ptr::null(), - libc::MS_REC | libc::MS_PRIVATE, - std::ptr::null(), - ) - }; - if result != 0 { - return Err(std::io::Error::last_os_error().into()); - } - Ok(()) -} - -/// Bind-mount a path onto itself and remount read-only. -fn bind_mount_read_only(path: &Path) -> Result<()> { - let c_path = CString::new(path.as_os_str().as_bytes()).map_err(|_| { - CodexErr::UnsupportedOperation(format!( - "Sandbox mount path contains NUL byte: {path}", - path = path.display() - )) - })?; - - let bind_result = unsafe { - libc::mount( - c_path.as_ptr(), - c_path.as_ptr(), - std::ptr::null(), - libc::MS_BIND, - std::ptr::null(), - ) - }; - if bind_result != 0 { - return Err(std::io::Error::last_os_error().into()); - } - - let remount_result = unsafe { - libc::mount( - c_path.as_ptr(), - c_path.as_ptr(), - std::ptr::null(), - libc::MS_BIND | libc::MS_REMOUNT | libc::MS_RDONLY, - std::ptr::null(), - ) - }; - if remount_result != 0 { - return Err(std::io::Error::last_os_error().into()); - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn collect_read_only_mount_targets_errors_on_missing_path() { - let tempdir = tempfile::tempdir().expect("tempdir"); - let missing = AbsolutePathBuf::try_from(tempdir.path().join("missing").as_path()) - .expect("missing path"); - let root = AbsolutePathBuf::try_from(tempdir.path()).expect("root"); - let writable_root = WritableRoot { - root, - read_only_subpaths: vec![missing], - }; - - let err = collect_read_only_mount_targets(&[writable_root]) - .expect_err("expected missing path error"); - let message = match err { - CodexErr::UnsupportedOperation(message) => message, - other => panic!("unexpected error: {other:?}"), - }; - assert_eq!( - message, - format!( - "Sandbox expected to protect {path}, but it does not exist. Ensure the repository contains this path or create it before running Codex.", - path = tempdir.path().join("missing").display() - ) - ); - } - - #[test] - fn collect_read_only_mount_targets_adds_gitdir_for_pointer_file() { - let tempdir = tempfile::tempdir().expect("tempdir"); - let gitdir = tempdir.path().join("actual-gitdir"); - std::fs::create_dir_all(&gitdir).expect("create gitdir"); - let dot_git = tempdir.path().join(".git"); - std::fs::write(&dot_git, format!("gitdir: {}\n", gitdir.display())) - .expect("write gitdir pointer"); - let root = AbsolutePathBuf::try_from(tempdir.path()).expect("root"); - let writable_root = WritableRoot { - root, - read_only_subpaths: vec![ - AbsolutePathBuf::try_from(dot_git.as_path()).expect("dot git"), - ], - }; - - let targets = collect_read_only_mount_targets(&[writable_root]).expect("collect targets"); - assert_eq!(targets.len(), 2); - assert_eq!(targets[0].as_path(), dot_git.as_path()); - assert_eq!(targets[1].as_path(), gitdir.as_path()); - } - - #[test] - fn collect_read_only_mount_targets_errors_on_invalid_gitdir_pointer() { - let tempdir = tempfile::tempdir().expect("tempdir"); - let dot_git = tempdir.path().join(".git"); - std::fs::write(&dot_git, "not-a-pointer\n").expect("write invalid pointer"); - let root = AbsolutePathBuf::try_from(tempdir.path()).expect("root"); - let writable_root = WritableRoot { - root, - read_only_subpaths: vec![ - AbsolutePathBuf::try_from(dot_git.as_path()).expect("dot git"), - ], - }; - - let err = collect_read_only_mount_targets(&[writable_root]) - .expect_err("expected invalid pointer error"); - let message = match err { - CodexErr::UnsupportedOperation(message) => message, - other => panic!("unexpected error: {other:?}"), - }; - assert_eq!( - message, - format!( - "Expected {path} to contain a gitdir pointer, but it did not match `gitdir: `.", - path = dot_git.display() - ) - ); - } -} diff --git a/codex-rs/linux-sandbox/src/vendored_bwrap.rs b/codex-rs/linux-sandbox/src/vendored_bwrap.rs index ab4fb959ef..c2816061ea 100644 --- a/codex-rs/linux-sandbox/src/vendored_bwrap.rs +++ b/codex-rs/linux-sandbox/src/vendored_bwrap.rs @@ -13,22 +13,35 @@ mod imp { fn bwrap_main(argc: libc::c_int, argv: *const *const c_char) -> libc::c_int; } - /// Execute the build-time bubblewrap `main` function with the given argv. - pub(crate) fn exec_vendored_bwrap(argv: Vec) -> ! { + fn argv_to_cstrings(argv: &[String]) -> Vec { let mut cstrings: Vec = Vec::with_capacity(argv.len()); - for arg in &argv { + for arg in argv { match CString::new(arg.as_str()) { Ok(value) => cstrings.push(value), Err(err) => panic!("failed to convert argv to CString: {err}"), } } + cstrings + } + + /// Run the build-time bubblewrap `main` function and return its exit code. + /// + /// On success, bubblewrap will `execve` into the target program and this + /// function will never return. A return value therefore implies failure. + pub(crate) fn run_vendored_bwrap_main(argv: &[String]) -> libc::c_int { + let cstrings = argv_to_cstrings(argv); let mut argv_ptrs: Vec<*const c_char> = cstrings.iter().map(|arg| arg.as_ptr()).collect(); argv_ptrs.push(std::ptr::null()); // SAFETY: We provide a null-terminated argv vector whose pointers // remain valid for the duration of the call. - let exit_code = unsafe { bwrap_main(cstrings.len() as libc::c_int, argv_ptrs.as_ptr()) }; + unsafe { bwrap_main(cstrings.len() as libc::c_int, argv_ptrs.as_ptr()) } + } + + /// Execute the build-time bubblewrap `main` function with the given argv. + pub(crate) fn exec_vendored_bwrap(argv: Vec) -> ! { + let exit_code = run_vendored_bwrap_main(&argv); std::process::exit(exit_code); } } @@ -36,7 +49,7 @@ mod imp { #[cfg(not(vendored_bwrap_available))] mod imp { /// Panics with a clear error when the build-time bwrap path is not enabled. - pub(crate) fn exec_vendored_bwrap(_argv: Vec) -> ! { + pub(crate) fn run_vendored_bwrap_main(_argv: &[String]) -> libc::c_int { panic!( "build-time bubblewrap is not available in this build.\n\ Rebuild codex-linux-sandbox on Linux with CODEX_BWRAP_ENABLE_FFI=1.\n\ @@ -49,6 +62,13 @@ Notes:\n\ - bubblewrap sources expected at codex-rs/vendor/bubblewrap (default)" ); } + + /// Panics with a clear error when the build-time bwrap path is not enabled. + pub(crate) fn exec_vendored_bwrap(_argv: Vec) -> ! { + let _ = run_vendored_bwrap_main(&[]); + unreachable!("run_vendored_bwrap_main should always panic in this configuration") + } } pub(crate) use imp::exec_vendored_bwrap; +pub(crate) use imp::run_vendored_bwrap_main; diff --git a/codex-rs/linux-sandbox/tests/suite/landlock.rs b/codex-rs/linux-sandbox/tests/suite/landlock.rs index 4d803b2297..a3a535ad75 100644 --- a/codex-rs/linux-sandbox/tests/suite/landlock.rs +++ b/codex-rs/linux-sandbox/tests/suite/landlock.rs @@ -2,6 +2,7 @@ #![allow(clippy::unwrap_used)] use codex_core::config::types::ShellEnvironmentPolicy; use codex_core::error::CodexErr; +use codex_core::error::Result; use codex_core::error::SandboxErr; use codex_core::exec::ExecParams; use codex_core::exec::process_exec_tool_call; @@ -32,6 +33,8 @@ const NETWORK_TIMEOUT_MS: u64 = 2_000; #[cfg(target_arch = "aarch64")] const NETWORK_TIMEOUT_MS: u64 = 10_000; +const BWRAP_UNAVAILABLE_ERR: &str = "build-time bubblewrap is not available in this build."; + fn create_env_from_core_vars() -> HashMap { let policy = ShellEnvironmentPolicy::default(); create_env(&policy, None) @@ -56,12 +59,22 @@ async fn run_cmd(cmd: &[&str], writable_roots: &[PathBuf], timeout_ms: u64) { } } -#[expect(clippy::expect_used, clippy::unwrap_used)] +#[expect(clippy::expect_used)] async fn run_cmd_output( cmd: &[&str], writable_roots: &[PathBuf], timeout_ms: u64, -) -> codex_core::error::Result { +) -> Result { + run_cmd_result_with_writable_roots(cmd, writable_roots, timeout_ms, false).await +} + +#[expect(clippy::expect_used)] +async fn run_cmd_result_with_writable_roots( + cmd: &[&str], + writable_roots: &[PathBuf], + timeout_ms: u64, + use_bwrap_sandbox: bool, +) -> Result { let cwd = std::env::current_dir().expect("cwd should exist"); let sandbox_cwd = cwd.clone(); let params = ExecParams { @@ -95,6 +108,7 @@ async fn run_cmd_output( &sandbox_policy, sandbox_cwd.as_path(), &codex_linux_sandbox_exe, + use_bwrap_sandbox, None, ) .await @@ -114,6 +128,44 @@ fn is_landlock_unavailable_error(err: &CodexErr) -> bool { } } +fn is_bwrap_unavailable_output(output: &codex_core::exec::ExecToolCallOutput) -> bool { + output.stderr.text.contains(BWRAP_UNAVAILABLE_ERR) +} + +async fn should_skip_bwrap_tests() -> bool { + match run_cmd_result_with_writable_roots( + &["bash", "-lc", "true"], + &[], + NETWORK_TIMEOUT_MS, + true, + ) + .await + { + Ok(output) => is_bwrap_unavailable_output(&output), + Err(CodexErr::Sandbox(SandboxErr::Denied { output })) => { + is_bwrap_unavailable_output(&output) + } + // Probe timeouts are not actionable for the bwrap-specific assertions below; + // skip rather than fail the whole suite. + Err(CodexErr::Sandbox(SandboxErr::Timeout { .. })) => true, + Err(err) => panic!("bwrap availability probe failed unexpectedly: {err:?}"), + } +} + +fn expect_denied( + result: Result, + context: &str, +) -> codex_core::exec::ExecToolCallOutput { + match result { + Ok(output) => { + assert_ne!(output.exit_code, 0, "{context}: expected nonzero exit code"); + output + } + Err(CodexErr::Sandbox(SandboxErr::Denied { output })) => *output, + Err(err) => panic!("{context}: {err:?}"), + } +} + #[tokio::test] async fn test_root_read() { run_cmd(&["ls", "-l", "/bin"], &[], SHORT_TIMEOUT_MS).await; @@ -242,6 +294,7 @@ async fn assert_network_blocked(cmd: &[&str]) { &sandbox_policy, sandbox_cwd.as_path(), &codex_linux_sandbox_exe, + false, None, ) .await; @@ -292,6 +345,90 @@ async fn sandbox_blocks_nc() { assert_network_blocked(&["nc", "-z", "127.0.0.1", "80"]).await; } +#[tokio::test] +async fn sandbox_blocks_git_and_codex_writes_inside_writable_root() { + if should_skip_bwrap_tests().await { + eprintln!("skipping bwrap test: vendored bwrap was not built in this environment"); + return; + } + + let tmpdir = tempfile::tempdir().expect("tempdir"); + let dot_git = tmpdir.path().join(".git"); + let dot_codex = tmpdir.path().join(".codex"); + std::fs::create_dir_all(&dot_git).expect("create .git"); + std::fs::create_dir_all(&dot_codex).expect("create .codex"); + + let git_target = dot_git.join("config"); + let codex_target = dot_codex.join("config.toml"); + + let git_output = expect_denied( + run_cmd_result_with_writable_roots( + &[ + "bash", + "-lc", + &format!("echo denied > {}", git_target.to_string_lossy()), + ], + &[tmpdir.path().to_path_buf()], + LONG_TIMEOUT_MS, + true, + ) + .await, + ".git write should be denied under bubblewrap", + ); + + let codex_output = expect_denied( + run_cmd_result_with_writable_roots( + &[ + "bash", + "-lc", + &format!("echo denied > {}", codex_target.to_string_lossy()), + ], + &[tmpdir.path().to_path_buf()], + LONG_TIMEOUT_MS, + true, + ) + .await, + ".codex write should be denied under bubblewrap", + ); + assert_ne!(git_output.exit_code, 0); + assert_ne!(codex_output.exit_code, 0); +} + +#[tokio::test] +async fn sandbox_blocks_codex_symlink_replacement_attack() { + if should_skip_bwrap_tests().await { + eprintln!("skipping bwrap test: vendored bwrap was not built in this environment"); + return; + } + + use std::os::unix::fs::symlink; + + let tmpdir = tempfile::tempdir().expect("tempdir"); + let decoy = tmpdir.path().join("decoy-codex"); + std::fs::create_dir_all(&decoy).expect("create decoy dir"); + + let dot_codex = tmpdir.path().join(".codex"); + symlink(&decoy, &dot_codex).expect("create .codex symlink"); + + let codex_target = dot_codex.join("config.toml"); + + let codex_output = expect_denied( + run_cmd_result_with_writable_roots( + &[ + "bash", + "-lc", + &format!("echo denied > {}", codex_target.to_string_lossy()), + ], + &[tmpdir.path().to_path_buf()], + LONG_TIMEOUT_MS, + true, + ) + .await, + ".codex symlink replacement should be denied", + ); + assert_ne!(codex_output.exit_code, 0); +} + #[tokio::test] async fn sandbox_blocks_ssh() { // Force ssh to attempt a real TCP connection but fail quickly. `BatchMode` diff --git a/codex-rs/otel/Cargo.toml b/codex-rs/otel/Cargo.toml index 0fbd2a8055..6e6321d2e5 100644 --- a/codex-rs/otel/Cargo.toml +++ b/codex-rs/otel/Cargo.toml @@ -21,8 +21,8 @@ disable-default-metrics-exporter = [] [dependencies] chrono = { workspace = true } -codex-app-server-protocol = { workspace = true } codex-utils-absolute-path = { workspace = true } +codex-utils-string = { workspace = true } codex-api = { workspace = true } codex-protocol = { workspace = true } eventsource-stream = { workspace = true } @@ -50,6 +50,7 @@ opentelemetry_sdk = { workspace = true, features = [ "trace", ] } http = { workspace = true } +os_info = { workspace = true } reqwest = { workspace = true, features = ["blocking", "rustls-tls"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/codex-rs/otel/src/lib.rs b/codex-rs/otel/src/lib.rs index aed9e15b8e..a86e5ef525 100644 --- a/codex-rs/otel/src/lib.rs +++ b/codex-rs/otel/src/lib.rs @@ -14,6 +14,7 @@ use crate::metrics::validation::validate_tag_key; use crate::metrics::validation::validate_tag_value; use crate::otel_provider::OtelProvider; use codex_protocol::ThreadId; +pub use codex_utils_string::sanitize_metric_tag_value; use opentelemetry_sdk::metrics::data::ResourceMetrics; use serde::Serialize; use std::time::Duration; @@ -30,6 +31,13 @@ pub enum ToolDecisionSource { User, } +/// Maps to core AuthMode to avoid a circular dependency on codex-core. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)] +pub enum TelemetryAuthMode { + ApiKey, + Chatgpt, +} + #[derive(Debug, Clone)] pub struct OtelEventMetadata { pub(crate) conversation_id: ThreadId, @@ -188,7 +196,7 @@ impl OtelManager { if !self.metrics_use_metadata_tags { return Ok(Vec::new()); } - let mut tags = Vec::with_capacity(6); + let mut tags = Vec::with_capacity(5); Self::push_metadata_tag(&mut tags, "auth_mode", self.metadata.auth_mode.as_deref())?; Self::push_metadata_tag( &mut tags, diff --git a/codex-rs/otel/src/metrics/client.rs b/codex-rs/otel/src/metrics/client.rs index 386f811422..417c1f4bd9 100644 --- a/codex-rs/otel/src/metrics/client.rs +++ b/codex-rs/otel/src/metrics/client.rs @@ -9,6 +9,7 @@ use crate::metrics::validation::validate_metric_name; use crate::metrics::validation::validate_tag_key; use crate::metrics::validation::validate_tag_value; use crate::metrics::validation::validate_tags; +use codex_utils_string::sanitize_metric_tag_value; use opentelemetry::KeyValue; use opentelemetry::metrics::Counter; use opentelemetry::metrics::Histogram; @@ -197,12 +198,17 @@ impl MetricsClient { validate_tags(&default_tags)?; + let mut resource_attributes = Vec::with_capacity(4); + resource_attributes.push(KeyValue::new( + semconv::attribute::SERVICE_VERSION, + service_version, + )); + resource_attributes.push(KeyValue::new(ENV_ATTRIBUTE, environment)); + resource_attributes.extend(os_resource_attributes()); + let resource = Resource::builder() .with_service_name(service_name) - .with_attributes(vec![ - KeyValue::new(semconv::attribute::SERVICE_VERSION, service_version), - KeyValue::new(ENV_ATTRIBUTE, environment), - ]) + .with_attributes(resource_attributes) .build(); let runtime_reader = runtime_reader.then(|| { @@ -284,6 +290,22 @@ impl MetricsClient { } } +fn os_resource_attributes() -> Vec { + let os_info = os_info::get(); + let os_type_raw = os_info.os_type().to_string(); + let os_type = sanitize_metric_tag_value(os_type_raw.as_str()); + let os_version_raw = os_info.version().to_string(); + let os_version = sanitize_metric_tag_value(os_version_raw.as_str()); + let mut attributes = Vec::new(); + if os_type != "unspecified" { + attributes.push(KeyValue::new("os", os_type)); + } + if os_version != "unspecified" { + attributes.push(KeyValue::new("os_version", os_version)); + } + attributes +} + fn build_provider( resource: Resource, exporter: E, diff --git a/codex-rs/otel/src/metrics/names.rs b/codex-rs/otel/src/metrics/names.rs index b8ff9364ad..76b46d2450 100644 --- a/codex-rs/otel/src/metrics/names.rs +++ b/codex-rs/otel/src/metrics/names.rs @@ -8,3 +8,15 @@ pub(crate) const WEBSOCKET_REQUEST_COUNT_METRIC: &str = "codex.websocket.request pub(crate) const WEBSOCKET_REQUEST_DURATION_METRIC: &str = "codex.websocket.request.duration_ms"; pub(crate) const WEBSOCKET_EVENT_COUNT_METRIC: &str = "codex.websocket.event"; pub(crate) const WEBSOCKET_EVENT_DURATION_METRIC: &str = "codex.websocket.event.duration_ms"; +pub(crate) const RESPONSES_API_OVERHEAD_DURATION_METRIC: &str = + "codex.responses_api_overhead.duration_ms"; +pub(crate) const RESPONSES_API_INFERENCE_TIME_DURATION_METRIC: &str = + "codex.responses_api_inference_time.duration_ms"; +pub(crate) const RESPONSES_API_ENGINE_IAPI_TTFT_DURATION_METRIC: &str = + "codex.responses_api_engine_iapi_ttft.duration_ms"; +pub(crate) const RESPONSES_API_ENGINE_SERVICE_TTFT_DURATION_METRIC: &str = + "codex.responses_api_engine_service_ttft.duration_ms"; +pub(crate) const RESPONSES_API_ENGINE_IAPI_TBT_DURATION_METRIC: &str = + "codex.responses_api_engine_iapi_tbt.duration_ms"; +pub(crate) const RESPONSES_API_ENGINE_SERVICE_TBT_DURATION_METRIC: &str = + "codex.responses_api_engine_service_tbt.duration_ms"; diff --git a/codex-rs/otel/src/metrics/runtime_metrics.rs b/codex-rs/otel/src/metrics/runtime_metrics.rs index dbd28010f6..dcac367d87 100644 --- a/codex-rs/otel/src/metrics/runtime_metrics.rs +++ b/codex-rs/otel/src/metrics/runtime_metrics.rs @@ -1,5 +1,11 @@ use crate::metrics::names::API_CALL_COUNT_METRIC; use crate::metrics::names::API_CALL_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_ENGINE_IAPI_TBT_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_ENGINE_IAPI_TTFT_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_ENGINE_SERVICE_TBT_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_ENGINE_SERVICE_TTFT_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_INFERENCE_TIME_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_OVERHEAD_DURATION_METRIC; use crate::metrics::names::SSE_EVENT_COUNT_METRIC; use crate::metrics::names::SSE_EVENT_DURATION_METRIC; use crate::metrics::names::TOOL_CALL_COUNT_METRIC; @@ -23,6 +29,11 @@ impl RuntimeMetricTotals { pub fn is_empty(self) -> bool { self.count == 0 && self.duration_ms == 0 } + + pub fn merge(&mut self, other: Self) { + self.count = self.count.saturating_add(other.count); + self.duration_ms = self.duration_ms.saturating_add(other.duration_ms); + } } #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] @@ -32,6 +43,12 @@ pub struct RuntimeMetricsSummary { pub streaming_events: RuntimeMetricTotals, pub websocket_calls: RuntimeMetricTotals, pub websocket_events: RuntimeMetricTotals, + pub responses_api_overhead_ms: u64, + pub responses_api_inference_time_ms: u64, + pub responses_api_engine_iapi_ttft_ms: u64, + pub responses_api_engine_service_ttft_ms: u64, + pub responses_api_engine_iapi_tbt_ms: u64, + pub responses_api_engine_service_tbt_ms: u64, } impl RuntimeMetricsSummary { @@ -41,6 +58,50 @@ impl RuntimeMetricsSummary { && self.streaming_events.is_empty() && self.websocket_calls.is_empty() && self.websocket_events.is_empty() + && self.responses_api_overhead_ms == 0 + && self.responses_api_inference_time_ms == 0 + && self.responses_api_engine_iapi_ttft_ms == 0 + && self.responses_api_engine_service_ttft_ms == 0 + && self.responses_api_engine_iapi_tbt_ms == 0 + && self.responses_api_engine_service_tbt_ms == 0 + } + + pub fn merge(&mut self, other: Self) { + self.tool_calls.merge(other.tool_calls); + self.api_calls.merge(other.api_calls); + self.streaming_events.merge(other.streaming_events); + self.websocket_calls.merge(other.websocket_calls); + self.websocket_events.merge(other.websocket_events); + if other.responses_api_overhead_ms > 0 { + self.responses_api_overhead_ms = other.responses_api_overhead_ms; + } + if other.responses_api_inference_time_ms > 0 { + self.responses_api_inference_time_ms = other.responses_api_inference_time_ms; + } + if other.responses_api_engine_iapi_ttft_ms > 0 { + self.responses_api_engine_iapi_ttft_ms = other.responses_api_engine_iapi_ttft_ms; + } + if other.responses_api_engine_service_ttft_ms > 0 { + self.responses_api_engine_service_ttft_ms = other.responses_api_engine_service_ttft_ms; + } + if other.responses_api_engine_iapi_tbt_ms > 0 { + self.responses_api_engine_iapi_tbt_ms = other.responses_api_engine_iapi_tbt_ms; + } + if other.responses_api_engine_service_tbt_ms > 0 { + self.responses_api_engine_service_tbt_ms = other.responses_api_engine_service_tbt_ms; + } + } + + pub fn responses_api_summary(&self) -> RuntimeMetricsSummary { + Self { + responses_api_overhead_ms: self.responses_api_overhead_ms, + responses_api_inference_time_ms: self.responses_api_inference_time_ms, + responses_api_engine_iapi_ttft_ms: self.responses_api_engine_iapi_ttft_ms, + responses_api_engine_service_ttft_ms: self.responses_api_engine_service_ttft_ms, + responses_api_engine_iapi_tbt_ms: self.responses_api_engine_iapi_tbt_ms, + responses_api_engine_service_tbt_ms: self.responses_api_engine_service_tbt_ms, + ..RuntimeMetricsSummary::default() + } } pub(crate) fn from_snapshot(snapshot: &ResourceMetrics) -> Self { @@ -64,12 +125,30 @@ impl RuntimeMetricsSummary { count: sum_counter(snapshot, WEBSOCKET_EVENT_COUNT_METRIC), duration_ms: sum_histogram_ms(snapshot, WEBSOCKET_EVENT_DURATION_METRIC), }; + let responses_api_overhead_ms = + sum_histogram_ms(snapshot, RESPONSES_API_OVERHEAD_DURATION_METRIC); + let responses_api_inference_time_ms = + sum_histogram_ms(snapshot, RESPONSES_API_INFERENCE_TIME_DURATION_METRIC); + let responses_api_engine_iapi_ttft_ms = + sum_histogram_ms(snapshot, RESPONSES_API_ENGINE_IAPI_TTFT_DURATION_METRIC); + let responses_api_engine_service_ttft_ms = + sum_histogram_ms(snapshot, RESPONSES_API_ENGINE_SERVICE_TTFT_DURATION_METRIC); + let responses_api_engine_iapi_tbt_ms = + sum_histogram_ms(snapshot, RESPONSES_API_ENGINE_IAPI_TBT_DURATION_METRIC); + let responses_api_engine_service_tbt_ms = + sum_histogram_ms(snapshot, RESPONSES_API_ENGINE_SERVICE_TBT_DURATION_METRIC); Self { tool_calls, api_calls, streaming_events, websocket_calls, websocket_events, + responses_api_overhead_ms, + responses_api_inference_time_ms, + responses_api_engine_iapi_ttft_ms, + responses_api_engine_service_ttft_ms, + responses_api_engine_iapi_tbt_ms, + responses_api_engine_service_tbt_ms, } } } diff --git a/codex-rs/otel/src/traces/otel_manager.rs b/codex-rs/otel/src/traces/otel_manager.rs index c585ec67d5..c54c64c6cb 100644 --- a/codex-rs/otel/src/traces/otel_manager.rs +++ b/codex-rs/otel/src/traces/otel_manager.rs @@ -1,5 +1,12 @@ +use crate::TelemetryAuthMode; use crate::metrics::names::API_CALL_COUNT_METRIC; use crate::metrics::names::API_CALL_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_ENGINE_IAPI_TBT_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_ENGINE_IAPI_TTFT_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_ENGINE_SERVICE_TBT_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_ENGINE_SERVICE_TTFT_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_INFERENCE_TIME_DURATION_METRIC; +use crate::metrics::names::RESPONSES_API_OVERHEAD_DURATION_METRIC; use crate::metrics::names::SSE_EVENT_COUNT_METRIC; use crate::metrics::names::SSE_EVENT_DURATION_METRIC; use crate::metrics::names::TOOL_CALL_COUNT_METRIC; @@ -13,7 +20,6 @@ use chrono::SecondsFormat; use chrono::Utc; use codex_api::ApiError; use codex_api::ResponseEvent; -use codex_app_server_protocol::AuthMode; use codex_protocol::ThreadId; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::models::ResponseItem; @@ -42,6 +48,14 @@ pub use crate::ToolDecisionSource; const SSE_UNKNOWN_KIND: &str = "unknown"; const WEBSOCKET_UNKNOWN_KIND: &str = "unknown"; +const RESPONSES_WEBSOCKET_TIMING_KIND: &str = "responsesapi.websocket_timing"; +const RESPONSES_WEBSOCKET_TIMING_METRICS_FIELD: &str = "timing_metrics"; +const RESPONSES_API_OVERHEAD_FIELD: &str = "responses_duration_excl_engine_and_client_tool_time_ms"; +const RESPONSES_API_INFERENCE_FIELD: &str = "engine_service_total_ms"; +const RESPONSES_API_ENGINE_IAPI_TTFT_FIELD: &str = "engine_iapi_ttft_total_ms"; +const RESPONSES_API_ENGINE_SERVICE_TTFT_FIELD: &str = "engine_service_ttft_total_ms"; +const RESPONSES_API_ENGINE_IAPI_TBT_FIELD: &str = "engine_iapi_tbt_across_engine_calls_ms"; +const RESPONSES_API_ENGINE_SERVICE_TBT_FIELD: &str = "engine_service_tbt_across_engine_calls_ms"; impl OtelManager { #[allow(clippy::too_many_arguments)] @@ -51,7 +65,7 @@ impl OtelManager { slug: &str, account_id: Option, account_email: Option, - auth_mode: Option, + auth_mode: Option, log_user_prompts: bool, terminal_type: String, session_source: SessionSource, @@ -252,6 +266,9 @@ impl OtelManager { .get("type") .and_then(|value| value.as_str()) .map(std::string::ToString::to_string); + if kind.as_deref() == Some(RESPONSES_WEBSOCKET_TIMING_KIND) { + self.record_responses_websocket_timing_metrics(&value); + } if kind.as_deref() == Some("response.failed") { success = false; error_message = value @@ -557,11 +574,12 @@ impl OtelManager { ); } - pub async fn log_tool_result( + pub async fn log_tool_result_with_tags( &self, tool_name: &str, call_id: &str, arguments: &str, + extra_tags: &[(&str, &str)], f: F, ) -> Result<(String, bool), E> where @@ -578,13 +596,14 @@ impl OtelManager { Err(error) => (Cow::Owned(error.to_string()), false), }; - self.tool_result( + self.tool_result_with_tags( tool_name, call_id, arguments, duration, success, output.as_ref(), + extra_tags, ); result @@ -610,7 +629,8 @@ impl OtelManager { ); } - pub fn tool_result( + #[allow(clippy::too_many_arguments)] + pub fn tool_result_with_tags( &self, tool_name: &str, call_id: &str, @@ -618,18 +638,15 @@ impl OtelManager { duration: Duration, success: bool, output: &str, + extra_tags: &[(&str, &str)], ) { let success_str = if success { "true" } else { "false" }; - self.counter( - TOOL_CALL_COUNT_METRIC, - 1, - &[("tool", tool_name), ("success", success_str)], - ); - self.record_duration( - TOOL_CALL_DURATION_METRIC, - duration, - &[("tool", tool_name), ("success", success_str)], - ); + let mut tags = Vec::with_capacity(2 + extra_tags.len()); + tags.push(("tool", tool_name)); + tags.push(("success", success_str)); + tags.extend_from_slice(extra_tags); + self.counter(TOOL_CALL_COUNT_METRIC, 1, &tags); + self.record_duration(TOOL_CALL_DURATION_METRIC, duration, &tags); tracing::event!( tracing::Level::INFO, event.name = "codex.tool_result", @@ -651,6 +668,58 @@ impl OtelManager { ); } + fn record_responses_websocket_timing_metrics(&self, value: &serde_json::Value) { + let timing_metrics = value.get(RESPONSES_WEBSOCKET_TIMING_METRICS_FIELD); + + let overhead_value = + timing_metrics.and_then(|value| value.get(RESPONSES_API_OVERHEAD_FIELD)); + if let Some(duration) = duration_from_ms_value(overhead_value) { + self.record_duration(RESPONSES_API_OVERHEAD_DURATION_METRIC, duration, &[]); + } + + let inference_value = + timing_metrics.and_then(|value| value.get(RESPONSES_API_INFERENCE_FIELD)); + if let Some(duration) = duration_from_ms_value(inference_value) { + self.record_duration(RESPONSES_API_INFERENCE_TIME_DURATION_METRIC, duration, &[]); + } + + let engine_iapi_ttft_value = + timing_metrics.and_then(|value| value.get(RESPONSES_API_ENGINE_IAPI_TTFT_FIELD)); + if let Some(duration) = duration_from_ms_value(engine_iapi_ttft_value) { + self.record_duration( + RESPONSES_API_ENGINE_IAPI_TTFT_DURATION_METRIC, + duration, + &[], + ); + } + + let engine_service_ttft_value = + timing_metrics.and_then(|value| value.get(RESPONSES_API_ENGINE_SERVICE_TTFT_FIELD)); + if let Some(duration) = duration_from_ms_value(engine_service_ttft_value) { + self.record_duration( + RESPONSES_API_ENGINE_SERVICE_TTFT_DURATION_METRIC, + duration, + &[], + ); + } + + let engine_iapi_tbt_value = + timing_metrics.and_then(|value| value.get(RESPONSES_API_ENGINE_IAPI_TBT_FIELD)); + if let Some(duration) = duration_from_ms_value(engine_iapi_tbt_value) { + self.record_duration(RESPONSES_API_ENGINE_IAPI_TBT_DURATION_METRIC, duration, &[]); + } + + let engine_service_tbt_value = + timing_metrics.and_then(|value| value.get(RESPONSES_API_ENGINE_SERVICE_TBT_FIELD)); + if let Some(duration) = duration_from_ms_value(engine_service_tbt_value) { + self.record_duration( + RESPONSES_API_ENGINE_SERVICE_TBT_DURATION_METRIC, + duration, + &[], + ); + } + } + fn responses_type(event: &ResponseEvent) -> String { match event { ResponseEvent::Created => "created".into(), @@ -689,3 +758,16 @@ impl OtelManager { fn timestamp() -> String { Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true) } + +fn duration_from_ms_value(value: Option<&serde_json::Value>) -> Option { + let value = value?; + let ms = value + .as_f64() + .or_else(|| value.as_i64().map(|v| v as f64)) + .or_else(|| value.as_u64().map(|v| v as f64))?; + if !ms.is_finite() || ms < 0.0 { + return None; + } + let clamped = ms.min(u64::MAX as f64); + Some(Duration::from_millis(clamped.round() as u64)) +} diff --git a/codex-rs/otel/tests/suite/manager_metrics.rs b/codex-rs/otel/tests/suite/manager_metrics.rs index ad98543bc1..164bb43b3a 100644 --- a/codex-rs/otel/tests/suite/manager_metrics.rs +++ b/codex-rs/otel/tests/suite/manager_metrics.rs @@ -2,8 +2,8 @@ use crate::harness::attributes_to_map; use crate::harness::build_metrics_with_defaults; use crate::harness::find_metric; use crate::harness::latest_metrics; -use codex_app_server_protocol::AuthMode; use codex_otel::OtelManager; +use codex_otel::TelemetryAuthMode; use codex_otel::metrics::Result; use codex_protocol::ThreadId; use codex_protocol::protocol::SessionSource; @@ -22,7 +22,7 @@ fn manager_attaches_metadata_tags_to_metrics() -> Result<()> { "gpt-5.1", Some("account-id".to_string()), None, - Some(AuthMode::ApiKey), + Some(TelemetryAuthMode::ApiKey), true, "tty".to_string(), SessionSource::Cli, @@ -52,7 +52,10 @@ fn manager_attaches_metadata_tags_to_metrics() -> Result<()> { "app.version".to_string(), env!("CARGO_PKG_VERSION").to_string(), ), - ("auth_mode".to_string(), AuthMode::ApiKey.to_string()), + ( + "auth_mode".to_string(), + TelemetryAuthMode::ApiKey.to_string(), + ), ("model".to_string(), "gpt-5.1".to_string()), ("service".to_string(), "codex-cli".to_string()), ("session_source".to_string(), "cli".to_string()), @@ -73,7 +76,7 @@ fn manager_allows_disabling_metadata_tags() -> Result<()> { "gpt-4o", Some("account-id".to_string()), None, - Some(AuthMode::ApiKey), + Some(TelemetryAuthMode::ApiKey), true, "tty".to_string(), SessionSource::Cli, diff --git a/codex-rs/otel/tests/suite/runtime_summary.rs b/codex-rs/otel/tests/suite/runtime_summary.rs index 78aed8a0a7..79c6b258e7 100644 --- a/codex-rs/otel/tests/suite/runtime_summary.rs +++ b/codex-rs/otel/tests/suite/runtime_summary.rs @@ -1,7 +1,7 @@ -use codex_app_server_protocol::AuthMode; use codex_otel::OtelManager; use codex_otel::RuntimeMetricTotals; use codex_otel::RuntimeMetricsSummary; +use codex_otel::TelemetryAuthMode; use codex_otel::metrics::MetricsClient; use codex_otel::metrics::MetricsConfig; use codex_otel::metrics::Result; @@ -26,7 +26,7 @@ fn runtime_metrics_summary_collects_tool_api_and_streaming_metrics() -> Result<( "gpt-5.1", Some("account-id".to_string()), None, - Some(AuthMode::ApiKey), + Some(TelemetryAuthMode::ApiKey), true, "tty".to_string(), SessionSource::Cli, @@ -35,13 +35,14 @@ fn runtime_metrics_summary_collects_tool_api_and_streaming_metrics() -> Result<( manager.reset_runtime_metrics(); - manager.tool_result( + manager.tool_result_with_tags( "shell", "call-1", "{\"cmd\":\"echo\"}", Duration::from_millis(250), true, "ok", + &[], ); manager.record_api_request(1, Some(200), None, Duration::from_millis(300)); manager.record_websocket_request(Duration::from_millis(400), None); @@ -62,6 +63,14 @@ fn runtime_metrics_summary_collects_tool_api_and_streaming_metrics() -> Result<( r#"{"type":"response.created"}"#.into(), )))); manager.record_websocket_event(&ws_response, Duration::from_millis(80)); + let ws_timing_response: std::result::Result< + Option>, + codex_api::ApiError, + > = Ok(Some(Ok(Message::Text( + r#"{"type":"responsesapi.websocket_timing","timing_metrics":{"responses_duration_excl_engine_and_client_tool_time_ms":124,"engine_service_total_ms":457,"engine_iapi_ttft_total_ms":211,"engine_service_ttft_total_ms":233,"engine_iapi_tbt_across_engine_calls_ms":377,"engine_service_tbt_across_engine_calls_ms":399}}"# + .into(), + )))); + manager.record_websocket_event(&ws_timing_response, Duration::from_millis(20)); let summary = manager .runtime_metrics_summary() @@ -84,9 +93,15 @@ fn runtime_metrics_summary_collects_tool_api_and_streaming_metrics() -> Result<( duration_ms: 400, }, websocket_events: RuntimeMetricTotals { - count: 1, - duration_ms: 80, + count: 2, + duration_ms: 100, }, + responses_api_overhead_ms: 124, + responses_api_inference_time_ms: 457, + responses_api_engine_iapi_ttft_ms: 211, + responses_api_engine_service_ttft_ms: 233, + responses_api_engine_iapi_tbt_ms: 377, + responses_api_engine_service_tbt_ms: 399, }; assert_eq!(summary, expected); diff --git a/codex-rs/otel/tests/suite/snapshot.rs b/codex-rs/otel/tests/suite/snapshot.rs index f0a7a18c54..aa7990d062 100644 --- a/codex-rs/otel/tests/suite/snapshot.rs +++ b/codex-rs/otel/tests/suite/snapshot.rs @@ -1,7 +1,7 @@ use crate::harness::attributes_to_map; use crate::harness::find_metric; -use codex_app_server_protocol::AuthMode; use codex_otel::OtelManager; +use codex_otel::TelemetryAuthMode; use codex_otel::metrics::MetricsClient; use codex_otel::metrics::MetricsConfig; use codex_otel::metrics::Result; @@ -75,7 +75,7 @@ fn manager_snapshot_metrics_collects_without_shutdown() -> Result<()> { "gpt-5.1", Some("account-id".to_string()), None, - Some(AuthMode::ApiKey), + Some(TelemetryAuthMode::ApiKey), true, "tty".to_string(), SessionSource::Cli, @@ -107,7 +107,10 @@ fn manager_snapshot_metrics_collects_without_shutdown() -> Result<()> { "app.version".to_string(), env!("CARGO_PKG_VERSION").to_string(), ), - ("auth_mode".to_string(), AuthMode::ApiKey.to_string()), + ( + "auth_mode".to_string(), + TelemetryAuthMode::ApiKey.to_string(), + ), ("model".to_string(), "gpt-5.1".to_string()), ("service".to_string(), "codex-cli".to_string()), ("session_source".to_string(), "cli".to_string()), diff --git a/codex-rs/protocol/src/config_types.rs b/codex-rs/protocol/src/config_types.rs index aa28e1a6fa..7586212adb 100644 --- a/codex-rs/protocol/src/config_types.rs +++ b/codex-rs/protocol/src/config_types.rs @@ -96,6 +96,7 @@ pub enum WindowsSandboxLevel { #[serde(rename_all = "lowercase")] #[strum(serialize_all = "lowercase")] pub enum Personality { + None, Friendly, Pragmatic, } @@ -192,6 +193,27 @@ pub enum ModeKind { Execute, } +pub const TUI_VISIBLE_COLLABORATION_MODES: [ModeKind; 2] = [ModeKind::Default, ModeKind::Plan]; + +impl ModeKind { + pub const fn display_name(self) -> &'static str { + match self { + Self::Plan => "Plan", + Self::Default => "Default", + Self::PairProgramming => "Pair Programming", + Self::Execute => "Execute", + } + } + + pub const fn is_tui_visible(self) -> bool { + matches!(self, Self::Plan | Self::Default) + } + + pub const fn allows_request_user_input(self) -> bool { + matches!(self, Self::Plan) + } +} + /// Collaboration mode for a Codex session. #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, JsonSchema, TS)] #[serde(rename_all = "lowercase")] @@ -323,4 +345,17 @@ mod tests { assert_eq!(ModeKind::Default, mode); } } + + #[test] + fn tui_visible_collaboration_modes_match_mode_kind_visibility() { + let expected = [ModeKind::Default, ModeKind::Plan]; + assert_eq!(expected, TUI_VISIBLE_COLLABORATION_MODES); + + for mode in TUI_VISIBLE_COLLABORATION_MODES { + assert!(mode.is_tui_visible()); + } + + assert!(!ModeKind::PairProgramming.is_tui_visible()); + assert!(!ModeKind::Execute.is_tui_visible()); + } } diff --git a/codex-rs/protocol/src/dynamic_tools.rs b/codex-rs/protocol/src/dynamic_tools.rs index e55d372d8e..8b5405f307 100644 --- a/codex-rs/protocol/src/dynamic_tools.rs +++ b/codex-rs/protocol/src/dynamic_tools.rs @@ -24,7 +24,16 @@ pub struct DynamicToolCallRequest { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] pub struct DynamicToolResponse { - pub call_id: String, - pub output: String, + pub content_items: Vec, pub success: bool, } + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema, TS)] +#[serde(tag = "type", rename_all = "camelCase")] +#[ts(tag = "type")] +pub enum DynamicToolCallOutputContentItem { + #[serde(rename_all = "camelCase")] + InputText { text: String }, + #[serde(rename_all = "camelCase")] + InputImage { image_url: String }, +} diff --git a/codex-rs/protocol/src/models.rs b/codex-rs/protocol/src/models.rs index fe63666e38..ae38a7890c 100644 --- a/codex-rs/protocol/src/models.rs +++ b/codex-rs/protocol/src/models.rs @@ -129,11 +129,11 @@ pub enum ResponseItem { arguments: String, call_id: String, }, - // NOTE: The input schema for `function_call_output` objects that clients send to the - // OpenAI /v1/responses endpoint is NOT the same shape as the objects the server returns on the - // SSE stream. When *sending* we must wrap the string output inside an object that includes a - // required `success` boolean. To ensure we serialize exactly the expected shape we introduce - // a dedicated payload struct and flatten it here. + // NOTE: The `output` field for `function_call_output` uses a dedicated payload type with + // custom serialization. On the wire it is either: + // - a plain string (`content`) + // - an array of structured content items (`content_items`) + // We keep this behavior centralized in `FunctionCallOutputPayload`. FunctionCallOutput { call_id: String, output: FunctionCallOutputPayload, @@ -249,7 +249,7 @@ impl DeveloperInstructions { match command_prefixes { Some(prefixes) => { format!( - "{APPROVAL_POLICY_ON_REQUEST_RULE}\nApproved command prefixes:\n{prefixes}" + "{APPROVAL_POLICY_ON_REQUEST_RULE}\n## Approved command prefixes\nThe following prefix rules have already been approved: {prefixes}" ) } None => APPROVAL_POLICY_ON_REQUEST_RULE.to_string(), @@ -274,6 +274,12 @@ impl DeveloperInstructions { Self { text } } + pub fn model_switch_message(model_instructions: String) -> Self { + DeveloperInstructions::new(format!( + "\nThe user was previously using a different model. Please continue the conversation according to the following instructions:\n\n{model_instructions}\n" + )) + } + pub fn personality_spec_message(spec: String) -> Self { let message = format!( " The user has requested a new communication style. Future messages should adhere to the following personality: \n{spec} " @@ -617,9 +623,8 @@ impl From for ResponseItem { let output = match result { Ok(result) => FunctionCallOutputPayload::from(&result), Err(tool_call_err) => FunctionCallOutputPayload { - content: format!("err: {tool_call_err:?}"), + body: FunctionCallOutputBody::Text(format!("err: {tool_call_err:?}")), success: Some(false), - ..Default::default() }, }; Self::FunctionCallOutput { call_id, output } @@ -780,39 +785,146 @@ pub enum FunctionCallOutputContentItem { InputImage { image_url: String }, } +/// Converts structured function-call output content into plain text for +/// human-readable surfaces. +/// +/// This conversion is intentionally lossy: +/// - only `input_text` items are included +/// - image items are ignored +/// +/// We use this helper where callers still need a string representation (for +/// example telemetry previews or legacy string-only output paths) while keeping +/// the original multimodal `content_items` as the authoritative payload sent to +/// the model. +pub fn function_call_output_content_items_to_text( + content_items: &[FunctionCallOutputContentItem], +) -> Option { + let text_segments = content_items + .iter() + .filter_map(|item| match item { + FunctionCallOutputContentItem::InputText { text } if !text.trim().is_empty() => { + Some(text.as_str()) + } + FunctionCallOutputContentItem::InputText { .. } + | FunctionCallOutputContentItem::InputImage { .. } => None, + }) + .collect::>(); + + if text_segments.is_empty() { + None + } else { + Some(text_segments.join("\n")) + } +} + +impl From + for FunctionCallOutputContentItem +{ + fn from(item: crate::dynamic_tools::DynamicToolCallOutputContentItem) -> Self { + match item { + crate::dynamic_tools::DynamicToolCallOutputContentItem::InputText { text } => { + Self::InputText { text } + } + crate::dynamic_tools::DynamicToolCallOutputContentItem::InputImage { image_url } => { + Self::InputImage { image_url } + } + } + } +} + /// The payload we send back to OpenAI when reporting a tool call result. /// -/// `content` preserves the historical plain-string payload so downstream -/// integrations (tests, logging, etc.) can keep treating tool output as -/// `String`. When an MCP server returns richer data we additionally populate -/// `content_items` with the structured form that the Responses API understands. +/// `body` serializes directly as the wire value for `function_call_output.output`. +/// `success` remains internal metadata for downstream handling. #[derive(Debug, Default, Clone, PartialEq, JsonSchema, TS)] pub struct FunctionCallOutputPayload { - pub content: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub content_items: Option>, + pub body: FunctionCallOutputBody, pub success: Option, } -#[derive(Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema, TS)] #[serde(untagged)] -enum FunctionCallOutputPayloadSerde { +pub enum FunctionCallOutputBody { Text(String), - Items(Vec), + ContentItems(Vec), } -// The Responses API expects two *different* shapes depending on success vs failure: -// • success → output is a plain string (no nested object) -// • failure → output is an object { content, success:false } +impl FunctionCallOutputBody { + /// Best-effort conversion of a function-call output body to plain text for + /// human-readable surfaces. + /// + /// This conversion is intentionally lossy when the body contains content + /// items: image entries are dropped and text entries are joined with + /// newlines. + pub fn to_text(&self) -> Option { + match self { + Self::Text(content) => Some(content.clone()), + Self::ContentItems(items) => function_call_output_content_items_to_text(items), + } + } +} + +impl Default for FunctionCallOutputBody { + fn default() -> Self { + Self::Text(String::new()) + } +} + +impl FunctionCallOutputPayload { + pub fn from_text(content: String) -> Self { + Self { + body: FunctionCallOutputBody::Text(content), + success: None, + } + } + + pub fn from_content_items(content_items: Vec) -> Self { + Self { + body: FunctionCallOutputBody::ContentItems(content_items), + success: None, + } + } + + pub fn text_content(&self) -> Option<&str> { + match &self.body { + FunctionCallOutputBody::Text(content) => Some(content), + FunctionCallOutputBody::ContentItems(_) => None, + } + } + + pub fn text_content_mut(&mut self) -> Option<&mut String> { + match &mut self.body { + FunctionCallOutputBody::Text(content) => Some(content), + FunctionCallOutputBody::ContentItems(_) => None, + } + } + + pub fn content_items(&self) -> Option<&[FunctionCallOutputContentItem]> { + match &self.body { + FunctionCallOutputBody::Text(_) => None, + FunctionCallOutputBody::ContentItems(items) => Some(items), + } + } + + pub fn content_items_mut(&mut self) -> Option<&mut Vec> { + match &mut self.body { + FunctionCallOutputBody::Text(_) => None, + FunctionCallOutputBody::ContentItems(items) => Some(items), + } + } +} + +// `function_call_output.output` is encoded as either: +// - an array of structured content items +// - a plain string impl Serialize for FunctionCallOutputPayload { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - if let Some(items) = &self.content_items { - items.serialize(serializer) - } else { - serializer.serialize_str(&self.content) + match &self.body { + FunctionCallOutputBody::Text(content) => serializer.serialize_str(content), + FunctionCallOutputBody::ContentItems(items) => items.serialize(serializer), } } } @@ -822,20 +934,11 @@ impl<'de> Deserialize<'de> for FunctionCallOutputPayload { where D: Deserializer<'de>, { - match FunctionCallOutputPayloadSerde::deserialize(deserializer)? { - FunctionCallOutputPayloadSerde::Text(content) => Ok(FunctionCallOutputPayload { - content, - ..Default::default() - }), - FunctionCallOutputPayloadSerde::Items(items) => { - let content = serde_json::to_string(&items).map_err(serde::de::Error::custom)?; - Ok(FunctionCallOutputPayload { - content, - content_items: Some(items), - success: None, - }) - } - } + let body = FunctionCallOutputBody::deserialize(deserializer)?; + Ok(FunctionCallOutputPayload { + body, + success: None, + }) } } @@ -856,16 +959,14 @@ impl From<&CallToolResult> for FunctionCallOutputPayload { match serde_json::to_string(structured_content) { Ok(serialized_structured_content) => { return FunctionCallOutputPayload { - content: serialized_structured_content, + body: FunctionCallOutputBody::Text(serialized_structured_content), success: Some(is_success), - ..Default::default() }; } Err(err) => { return FunctionCallOutputPayload { - content: err.to_string(), + body: FunctionCallOutputBody::Text(err.to_string()), success: Some(false), - ..Default::default() }; } } @@ -875,18 +976,21 @@ impl From<&CallToolResult> for FunctionCallOutputPayload { Ok(serialized_content) => serialized_content, Err(err) => { return FunctionCallOutputPayload { - content: err.to_string(), + body: FunctionCallOutputBody::Text(err.to_string()), success: Some(false), - ..Default::default() }; } }; let content_items = convert_mcp_content_to_items(content); + let body = match content_items { + Some(content_items) => FunctionCallOutputBody::ContentItems(content_items), + None => FunctionCallOutputBody::Text(serialized_content), + }; + FunctionCallOutputPayload { - content: serialized_content, - content_items, + body, success: Some(is_success), } } @@ -937,19 +1041,18 @@ fn convert_mcp_content_to_items( } // Implement Display so callers can treat the payload like a plain string when logging or doing -// trivial substring checks in tests (existing tests call `.contains()` on the output). Display -// returns the raw `content` field. +// trivial substring checks in tests (existing tests call `.contains()` on the output). For +// `ContentItems`, Display emits a JSON representation. impl std::fmt::Display for FunctionCallOutputPayload { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.content) - } -} - -impl std::ops::Deref for FunctionCallOutputPayload { - type Target = str; - fn deref(&self) -> &Self::Target { - &self.content + match &self.body { + FunctionCallOutputBody::Text(content) => f.write_str(content), + FunctionCallOutputBody::ContentItems(items) => { + let content = serde_json::to_string(items).unwrap_or_default(); + f.write_str(content.as_str()) + } + } } } @@ -1010,6 +1113,61 @@ mod tests { assert_eq!(convert_mcp_content_to_items(&contents), None); } + #[test] + fn function_call_output_content_items_to_text_joins_text_segments() { + let content_items = vec![ + FunctionCallOutputContentItem::InputText { + text: "line 1".to_string(), + }, + FunctionCallOutputContentItem::InputImage { + image_url: "data:image/png;base64,AAA".to_string(), + }, + FunctionCallOutputContentItem::InputText { + text: "line 2".to_string(), + }, + ]; + + let text = function_call_output_content_items_to_text(&content_items); + assert_eq!(text, Some("line 1\nline 2".to_string())); + } + + #[test] + fn function_call_output_content_items_to_text_ignores_blank_text_and_images() { + let content_items = vec![ + FunctionCallOutputContentItem::InputText { + text: " ".to_string(), + }, + FunctionCallOutputContentItem::InputImage { + image_url: "data:image/png;base64,AAA".to_string(), + }, + ]; + + let text = function_call_output_content_items_to_text(&content_items); + assert_eq!(text, None); + } + + #[test] + fn function_call_output_body_to_text_returns_plain_text_content() { + let body = FunctionCallOutputBody::Text("ok".to_string()); + let text = body.to_text(); + assert_eq!(text, Some("ok".to_string())); + } + + #[test] + fn function_call_output_body_to_text_uses_content_item_fallback() { + let body = FunctionCallOutputBody::ContentItems(vec![ + FunctionCallOutputContentItem::InputText { + text: "line 1".to_string(), + }, + FunctionCallOutputContentItem::InputImage { + image_url: "data:image/png;base64,AAA".to_string(), + }, + ]); + + let text = body.to_text(); + assert_eq!(text, Some("line 1".to_string())); + } + #[test] fn converts_sandbox_mode_into_developer_instructions() { let workspace_write: DeveloperInstructions = SandboxMode::WorkspaceWrite.into(); @@ -1156,10 +1314,7 @@ mod tests { fn serializes_success_as_plain_string() -> Result<()> { let item = ResponseInputItem::FunctionCallOutput { call_id: "call1".into(), - output: FunctionCallOutputPayload { - content: "ok".into(), - ..Default::default() - }, + output: FunctionCallOutputPayload::from_text("ok".into()), }; let json = serde_json::to_string(&item)?; @@ -1175,9 +1330,8 @@ mod tests { let item = ResponseInputItem::FunctionCallOutput { call_id: "call1".into(), output: FunctionCallOutputPayload { - content: "bad".into(), + body: FunctionCallOutputBody::Text("bad".into()), success: Some(false), - ..Default::default() }, }; @@ -1202,7 +1356,10 @@ mod tests { let payload = FunctionCallOutputPayload::from(&call_tool_result); assert_eq!(payload.success, Some(true)); - let items = payload.content_items.clone().expect("content items"); + let Some(items) = payload.content_items() else { + panic!("expected content items"); + }; + let items = items.to_vec(); assert_eq!( items, vec![ @@ -1243,9 +1400,10 @@ mod tests { }; let payload = FunctionCallOutputPayload::from(&call_tool_result); - let Some(items) = payload.content_items else { + let Some(items) = payload.content_items() else { panic!("expected content items"); }; + let items = items.to_vec(); assert_eq!( items, vec![FunctionCallOutputContentItem::InputImage { @@ -1274,10 +1432,14 @@ mod tests { image_url: "data:image/png;base64,XYZ".into(), }, ]; - assert_eq!(payload.content_items, Some(expected_items.clone())); - - let expected_content = serde_json::to_string(&expected_items)?; - assert_eq!(payload.content, expected_content); + assert_eq!( + payload.body, + FunctionCallOutputBody::ContentItems(expected_items.clone()) + ); + assert_eq!( + serde_json::to_string(&payload)?, + serde_json::to_string(&expected_items)? + ); Ok(()) } diff --git a/codex-rs/protocol/src/openai_models.rs b/codex-rs/protocol/src/openai_models.rs index 90cf34f393..36298e9ca5 100644 --- a/codex-rs/protocol/src/openai_models.rs +++ b/codex-rs/protocol/src/openai_models.rs @@ -335,6 +335,7 @@ impl ModelInstructionsVariables { pub fn get_personality_message(&self, personality: Option) -> Option { if let Some(personality) = personality { match personality { + Personality::None => Some(String::new()), Personality::Friendly => self.personality_friendly.clone(), Personality::Pragmatic => self.personality_pragmatic.clone(), } @@ -546,6 +547,10 @@ mod tests { model.get_model_instructions(Some(Personality::Pragmatic)), "Hello\n" ); + assert_eq!( + model.get_model_instructions(Some(Personality::None)), + "Hello\n" + ); assert_eq!(model.get_model_instructions(None), "Hello\n"); let model_no_personality = test_model(Some(ModelMessages { @@ -564,6 +569,10 @@ mod tests { model_no_personality.get_model_instructions(Some(Personality::Pragmatic)), "Hello\n" ); + assert_eq!( + model_no_personality.get_model_instructions(Some(Personality::None)), + "Hello\n" + ); assert_eq!(model_no_personality.get_model_instructions(None), "Hello\n"); } @@ -603,6 +612,10 @@ mod tests { personality_variables.get_personality_message(Some(Personality::Pragmatic)), Some("pragmatic".to_string()) ); + assert_eq!( + personality_variables.get_personality_message(Some(Personality::None)), + Some(String::new()) + ); assert_eq!( personality_variables.get_personality_message(None), Some("default".to_string()) @@ -621,6 +634,10 @@ mod tests { personality_variables.get_personality_message(Some(Personality::Pragmatic)), None ); + assert_eq!( + personality_variables.get_personality_message(Some(Personality::None)), + Some(String::new()) + ); assert_eq!( personality_variables.get_personality_message(None), Some("default".to_string()) @@ -639,6 +656,10 @@ mod tests { personality_variables.get_personality_message(Some(Personality::Pragmatic)), Some("pragmatic".to_string()) ); + assert_eq!( + personality_variables.get_personality_message(Some(Personality::None)), + Some(String::new()) + ); assert_eq!(personality_variables.get_personality_message(None), None); } } diff --git a/codex-rs/protocol/src/prompts/permissions/approval_policy/on_request_rule.md b/codex-rs/protocol/src/prompts/permissions/approval_policy/on_request_rule.md index 336f1fb748..96d962d12d 100644 --- a/codex-rs/protocol/src/prompts/permissions/approval_policy/on_request_rule.md +++ b/codex-rs/protocol/src/prompts/permissions/approval_policy/on_request_rule.md @@ -25,9 +25,9 @@ IMPORTANT: To request approval to execute a command that will require escalated - Provide the `sandbox_permissions` parameter with the value `"require_escalated"` - Include a short question asking the user if they want to allow the action in `justification` parameter. e.g. "Do you want to download and install dependencies for this project?" -- Suggest a `prefix_rule` - this will be shown to the user with an option to persist the rule approval for future sessions. +- Optionally suggest a `prefix_rule` - this will be shown to the user with an option to persist the rule approval for future sessions. -If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with "require_escalated". ALWAYS proceed to use the `justification` and `prefix_rule` parameters - do not message the user before requesting approval for the command. +If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with "require_escalated". ALWAYS proceed to use the `justification` parameter - do not message the user before requesting approval for the command. ## When to request escalation @@ -37,25 +37,21 @@ While commands are running inside the sandbox, here are some scenarios that will - You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files. - If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with `require_escalated`. ALWAYS proceed to use the `sandbox_permissions` and `justification` parameters. do not message the user before requesting approval for the command. - You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for. - -Only run commands that require approval if it is absolutely necessary to solve the user's query, don't try and circumvent approvals by using other tools. +- Be judicious with escalating, but if completing the user's request requires it, you should do so - don't try and circumvent approvals by using other tools. ## prefix_rule guidance -When choosing a `prefix_rule`, request one that will allow you to fulfill similar requests from the user in the future without re-requesting escalation. It should be categorical and reasonably scoped to similar capabilities. You MUST NOT pass the entire command into `prefix_rule`. +When choosing a `prefix_rule`, request one that will allow you to fulfill similar requests from the user in the future without re-requesting escalation. It should be categorical and reasonably scoped to similar capabilities. You should rarely pass the entire command into `prefix_rule`. + +### Banned prefix_rules +Avoid requesting overly broad prefixes that the user would be ill-advised to approve. For example, do not request ["python3"], ["python", "-"], or other similar prefixes. +NEVER provide a prefix_rule argument for destructive commands like rm. +NEVER provide a prefix_rule if your command uses a heredoc or herestring. + +### Examples +Good examples of prefixes: +- ["npm", "run", "dev"] +- ["gh", "pr", "check"] +- ["pytest"] +- ["cargo", "test"] - -["npm", "run", "dev"] - - -["gh", "pr", "checks"] - - -["pytest"] - - -["cargo", "test", "-p", "codex-app-server"] - -["cargo", "test"] - - diff --git a/codex-rs/state/migrations/0005_threads_cli_version.sql b/codex-rs/state/migrations/0005_threads_cli_version.sql new file mode 100644 index 0000000000..8891562d90 --- /dev/null +++ b/codex-rs/state/migrations/0005_threads_cli_version.sql @@ -0,0 +1 @@ +ALTER TABLE threads ADD COLUMN cli_version TEXT NOT NULL DEFAULT ''; diff --git a/codex-rs/state/migrations/0006_thread_memory.sql b/codex-rs/state/migrations/0006_thread_memory.sql new file mode 100644 index 0000000000..fe90ab6679 --- /dev/null +++ b/codex-rs/state/migrations/0006_thread_memory.sql @@ -0,0 +1,9 @@ +CREATE TABLE thread_memory ( + thread_id TEXT PRIMARY KEY, + trace_summary TEXT NOT NULL, + memory_summary TEXT NOT NULL, + updated_at INTEGER NOT NULL, + FOREIGN KEY(thread_id) REFERENCES threads(id) ON DELETE CASCADE +); + +CREATE INDEX idx_thread_memory_updated_at ON thread_memory(updated_at DESC, thread_id DESC); diff --git a/codex-rs/state/migrations/0007_threads_first_user_message.sql b/codex-rs/state/migrations/0007_threads_first_user_message.sql new file mode 100644 index 0000000000..5e9a7649bb --- /dev/null +++ b/codex-rs/state/migrations/0007_threads_first_user_message.sql @@ -0,0 +1,5 @@ +ALTER TABLE threads ADD COLUMN first_user_message TEXT NOT NULL DEFAULT ''; + +UPDATE threads +SET first_user_message = title +WHERE first_user_message = '' AND has_user_event = 1 AND title <> ''; diff --git a/codex-rs/state/migrations/0008_backfill_state.sql b/codex-rs/state/migrations/0008_backfill_state.sql new file mode 100644 index 0000000000..c9fc1fdeb7 --- /dev/null +++ b/codex-rs/state/migrations/0008_backfill_state.sql @@ -0,0 +1,17 @@ +CREATE TABLE backfill_state ( + id INTEGER PRIMARY KEY CHECK (id = 1), + status TEXT NOT NULL, + last_watermark TEXT, + last_success_at INTEGER, + updated_at INTEGER NOT NULL +); + +INSERT INTO backfill_state (id, status, last_watermark, last_success_at, updated_at) +VALUES ( + 1, + 'pending', + NULL, + NULL, + CAST(strftime('%s', 'now') AS INTEGER) +) +ON CONFLICT(id) DO NOTHING; diff --git a/codex-rs/state/src/bin/logs_client.rs b/codex-rs/state/src/bin/logs_client.rs index d1329a1a89..796a968cb8 100644 --- a/codex-rs/state/src/bin/logs_client.rs +++ b/codex-rs/state/src/bin/logs_client.rs @@ -6,14 +6,13 @@ use chrono::DateTime; use clap::Parser; use codex_state::LogQuery; use codex_state::LogRow; -use codex_state::STATE_DB_FILENAME; use codex_state::StateRuntime; use dirs::home_dir; use owo_colors::OwoColorize; #[derive(Debug, Parser)] #[command(name = "codex-state-logs")] -#[command(about = "Tail Codex logs from state.sqlite with simple filters")] +#[command(about = "Tail Codex logs from the state SQLite DB with simple filters")] struct Args { /// Path to CODEX_HOME. Defaults to $CODEX_HOME or ~/.codex. #[arg(long, env = "CODEX_HOME")] @@ -104,7 +103,7 @@ fn resolve_db_path(args: &Args) -> anyhow::Result { } let codex_home = args.codex_home.clone().unwrap_or_else(default_codex_home); - Ok(codex_home.join(STATE_DB_FILENAME)) + Ok(codex_state::state_db_path(codex_home.as_path())) } fn default_codex_home() -> PathBuf { diff --git a/codex-rs/state/src/extract.rs b/codex-rs/state/src/extract.rs index ad40118086..f8f9cb5251 100644 --- a/codex-rs/state/src/extract.rs +++ b/codex-rs/state/src/extract.rs @@ -1,16 +1,16 @@ use crate::model::ThreadMetadata; -use codex_protocol::models::ContentItem; use codex_protocol::models::ResponseItem; -use codex_protocol::models::is_local_image_close_tag_text; -use codex_protocol::models::is_local_image_open_tag_text; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::SessionMetaLine; use codex_protocol::protocol::TurnContextItem; use codex_protocol::protocol::USER_MESSAGE_BEGIN; +use codex_protocol::protocol::UserMessageEvent; use serde::Serialize; use serde_json::Value; +const IMAGE_ONLY_USER_MESSAGE_PLACEHOLDER: &str = "[Image]"; + /// Apply a rollout item to the metadata structure. pub fn apply_rollout_item( metadata: &mut ThreadMetadata, @@ -40,6 +40,9 @@ fn apply_session_meta_from_item(metadata: &mut ThreadMetadata, meta_line: &Sessi if let Some(provider) = meta_line.meta.model_provider.as_deref() { metadata.model_provider = provider.to_string(); } + if !meta_line.meta.cli_version.is_empty() { + metadata.cli_version = meta_line.meta.cli_version.clone(); + } if !meta_line.meta.cwd.as_os_str().is_empty() { metadata.cwd = meta_line.meta.cwd.clone(); } @@ -64,48 +67,22 @@ fn apply_event_msg(metadata: &mut ThreadMetadata, event: &EventMsg) { } } EventMsg::UserMessage(user) => { - metadata.has_user_event = true; + if metadata.first_user_message.is_none() { + metadata.first_user_message = user_message_preview(user); + } if metadata.title.is_empty() { - metadata.title = strip_user_message_prefix(user.message.as_str()).to_string(); + let title = strip_user_message_prefix(user.message.as_str()); + if !title.is_empty() { + metadata.title = title.to_string(); + } } } _ => {} } } -fn apply_response_item(metadata: &mut ThreadMetadata, item: &ResponseItem) { - if let Some(text) = extract_user_message_text(item) { - metadata.has_user_event = true; - if metadata.title.is_empty() { - metadata.title = text; - } - } -} - -fn extract_user_message_text(item: &ResponseItem) -> Option { - let ResponseItem::Message { role, content, .. } = item else { - return None; - }; - if role != "user" { - return None; - } - let texts: Vec<&str> = content - .iter() - .filter_map(|content_item| match content_item { - ContentItem::InputText { text } => Some(text.as_str()), - ContentItem::InputImage { .. } | ContentItem::OutputText { .. } => None, - }) - .filter(|text| !is_local_image_open_tag_text(text) && !is_local_image_close_tag_text(text)) - .collect(); - if texts.is_empty() { - return None; - } - let joined = texts.join("\n"); - Some( - strip_user_message_prefix(joined.as_str()) - .trim() - .to_string(), - ) +fn apply_response_item(_metadata: &mut ThreadMetadata, _item: &ResponseItem) { + // Title and first_user_message are derived from EventMsg::UserMessage only. } fn strip_user_message_prefix(text: &str) -> &str { @@ -115,6 +92,22 @@ fn strip_user_message_prefix(text: &str) -> &str { } } +fn user_message_preview(user: &UserMessageEvent) -> Option { + let message = strip_user_message_prefix(user.message.as_str()); + if !message.is_empty() { + return Some(message.to_string()); + } + if user + .images + .as_ref() + .is_some_and(|images| !images.is_empty()) + || !user.local_images.is_empty() + { + return Some(IMAGE_ONLY_USER_MESSAGE_PLACEHOLDER.to_string()); + } + None +} + pub(crate) fn enum_to_string(value: &T) -> String { match serde_json::to_value(value) { Ok(Value::String(s)) => s, @@ -125,43 +118,99 @@ pub(crate) fn enum_to_string(value: &T) -> String { #[cfg(test)] mod tests { - use super::extract_user_message_text; + use super::apply_rollout_item; use crate::model::ThreadMetadata; use chrono::DateTime; use chrono::Utc; use codex_protocol::ThreadId; use codex_protocol::models::ContentItem; use codex_protocol::models::ResponseItem; + use codex_protocol::protocol::EventMsg; + use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::USER_MESSAGE_BEGIN; + use codex_protocol::protocol::UserMessageEvent; + use pretty_assertions::assert_eq; use std::path::PathBuf; use uuid::Uuid; #[test] - fn extracts_user_message_text() { - let item = ResponseItem::Message { + fn response_item_user_messages_do_not_set_title_or_first_user_message() { + let mut metadata = metadata_for_test(); + let item = RolloutItem::ResponseItem(ResponseItem::Message { id: None, role: "user".to_string(), - content: vec![ - ContentItem::InputText { - text: format!(" {USER_MESSAGE_BEGIN}actual question"), - }, - ContentItem::InputImage { - image_url: "https://example.com/image.png".to_string(), - }, - ], + content: vec![ContentItem::InputText { + text: "hello from response item".to_string(), + }], end_turn: None, phase: None, - }; - let actual = extract_user_message_text(&item); - assert_eq!(actual.as_deref(), Some("actual question")); + }); + + apply_rollout_item(&mut metadata, &item, "test-provider"); + + assert_eq!(metadata.first_user_message, None); + assert_eq!(metadata.title, ""); } #[test] - fn diff_fields_detects_changes() { - let id = ThreadId::from_string(&Uuid::now_v7().to_string()).expect("thread id"); + fn event_msg_user_messages_set_title_and_first_user_message() { + let mut metadata = metadata_for_test(); + let item = RolloutItem::EventMsg(EventMsg::UserMessage(UserMessageEvent { + message: format!("{USER_MESSAGE_BEGIN} actual user request"), + images: Some(vec![]), + local_images: vec![], + text_elements: vec![], + })); + + apply_rollout_item(&mut metadata, &item, "test-provider"); + + assert_eq!( + metadata.first_user_message.as_deref(), + Some("actual user request") + ); + assert_eq!(metadata.title, "actual user request"); + } + + #[test] + fn event_msg_image_only_user_message_sets_image_placeholder_preview() { + let mut metadata = metadata_for_test(); + let item = RolloutItem::EventMsg(EventMsg::UserMessage(UserMessageEvent { + message: String::new(), + images: Some(vec!["https://example.com/image.png".to_string()]), + local_images: vec![], + text_elements: vec![], + })); + + apply_rollout_item(&mut metadata, &item, "test-provider"); + + assert_eq!( + metadata.first_user_message.as_deref(), + Some(super::IMAGE_ONLY_USER_MESSAGE_PLACEHOLDER) + ); + assert_eq!(metadata.title, ""); + } + + #[test] + fn event_msg_blank_user_message_without_images_keeps_first_user_message_empty() { + let mut metadata = metadata_for_test(); + let item = RolloutItem::EventMsg(EventMsg::UserMessage(UserMessageEvent { + message: " ".to_string(), + images: Some(vec![]), + local_images: vec![], + text_elements: vec![], + })); + + apply_rollout_item(&mut metadata, &item, "test-provider"); + + assert_eq!(metadata.first_user_message, None); + assert_eq!(metadata.title, ""); + } + + fn metadata_for_test() -> ThreadMetadata { + let id = ThreadId::from_string(&Uuid::from_u128(42).to_string()).expect("thread id"); let created_at = DateTime::::from_timestamp(1_735_689_600, 0).expect("timestamp"); - let base = ThreadMetadata { + ThreadMetadata { id, rollout_path: PathBuf::from("/tmp/a.jsonl"), created_at, @@ -169,16 +218,24 @@ mod tests { source: "cli".to_string(), model_provider: "openai".to_string(), cwd: PathBuf::from("/tmp"), - title: "hello".to_string(), + cli_version: "0.0.0".to_string(), + title: String::new(), sandbox_policy: "read-only".to_string(), approval_mode: "on-request".to_string(), tokens_used: 1, - has_user_event: false, + first_user_message: None, archived_at: None, git_sha: None, git_branch: None, git_origin_url: None, - }; + } + } + + #[test] + fn diff_fields_detects_changes() { + let mut base = metadata_for_test(); + base.id = ThreadId::from_string(&Uuid::now_v7().to_string()).expect("thread id"); + base.title = "hello".to_string(); let mut other = base.clone(); other.tokens_used = 2; other.title = "world".to_string(); diff --git a/codex-rs/state/src/lib.rs b/codex-rs/state/src/lib.rs index c08c76a1cf..1625554e29 100644 --- a/codex-rs/state/src/lib.rs +++ b/codex-rs/state/src/lib.rs @@ -22,19 +22,25 @@ pub use runtime::StateRuntime; /// Most consumers should prefer [`StateRuntime`]. pub use extract::apply_rollout_item; pub use model::Anchor; +pub use model::BackfillState; pub use model::BackfillStats; +pub use model::BackfillStatus; pub use model::ExtractionOutcome; pub use model::SortKey; +pub use model::ThreadMemory; pub use model::ThreadMetadata; pub use model::ThreadMetadataBuilder; pub use model::ThreadsPage; pub use runtime::STATE_DB_FILENAME; +pub use runtime::STATE_DB_VERSION; +pub use runtime::state_db_filename; +pub use runtime::state_db_path; /// Errors encountered during DB operations. Tags: [stage] pub const DB_ERROR_METRIC: &str = "codex.db.error"; -/// Metrics on backfill process during first init of the db. Tags: [status] +/// Metrics on backfill process. Tags: [status] pub const DB_METRIC_BACKFILL: &str = "codex.db.backfill"; -/// Metrics on backfill duration during first init of the db. Tags: [status] +/// Metrics on backfill duration. Tags: [status] pub const DB_METRIC_BACKFILL_DURATION_MS: &str = "codex.db.backfill.duration_ms"; /// Metrics on errors during comparison between DB and rollout file. Tags: [stage] pub const DB_METRIC_COMPARE_ERROR: &str = "codex.db.compare_error"; diff --git a/codex-rs/state/src/model/backfill_state.rs b/codex-rs/state/src/model/backfill_state.rs new file mode 100644 index 0000000000..353929f980 --- /dev/null +++ b/codex-rs/state/src/model/backfill_state.rs @@ -0,0 +1,73 @@ +use anyhow::Result; +use chrono::DateTime; +use chrono::Utc; +use sqlx::Row; +use sqlx::sqlite::SqliteRow; + +/// Persisted lifecycle state for rollout metadata backfill. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BackfillState { + /// Current lifecycle status. + pub status: BackfillStatus, + /// Last processed rollout watermark. + pub last_watermark: Option, + /// Last successful completion time. + pub last_success_at: Option>, +} + +impl Default for BackfillState { + fn default() -> Self { + Self { + status: BackfillStatus::Pending, + last_watermark: None, + last_success_at: None, + } + } +} + +impl BackfillState { + pub(crate) fn try_from_row(row: &SqliteRow) -> Result { + let status: String = row.try_get("status")?; + let last_success_at = row + .try_get::, _>("last_success_at")? + .map(epoch_seconds_to_datetime) + .transpose()?; + Ok(Self { + status: BackfillStatus::parse(status.as_str())?, + last_watermark: row.try_get("last_watermark")?, + last_success_at, + }) + } +} + +/// Backfill lifecycle status. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BackfillStatus { + Pending, + Running, + Complete, +} + +impl BackfillStatus { + pub const fn as_str(self) -> &'static str { + match self { + BackfillStatus::Pending => "pending", + BackfillStatus::Running => "running", + BackfillStatus::Complete => "complete", + } + } + + pub fn parse(value: &str) -> Result { + match value { + "pending" => Ok(Self::Pending), + "running" => Ok(Self::Running), + "complete" => Ok(Self::Complete), + _ => Err(anyhow::anyhow!("invalid backfill status: {value}")), + } + } +} + +fn epoch_seconds_to_datetime(secs: i64) -> Result> { + DateTime::::from_timestamp(secs, 0) + .ok_or_else(|| anyhow::anyhow!("invalid unix timestamp: {secs}")) +} diff --git a/codex-rs/state/src/model/mod.rs b/codex-rs/state/src/model/mod.rs index bd615d7561..6bec8875dc 100644 --- a/codex-rs/state/src/model/mod.rs +++ b/codex-rs/state/src/model/mod.rs @@ -1,9 +1,14 @@ +mod backfill_state; mod log; +mod thread_memory; mod thread_metadata; +pub use backfill_state::BackfillState; +pub use backfill_state::BackfillStatus; pub use log::LogEntry; pub use log::LogQuery; pub use log::LogRow; +pub use thread_memory::ThreadMemory; pub use thread_metadata::Anchor; pub use thread_metadata::BackfillStats; pub use thread_metadata::ExtractionOutcome; @@ -12,6 +17,7 @@ pub use thread_metadata::ThreadMetadata; pub use thread_metadata::ThreadMetadataBuilder; pub use thread_metadata::ThreadsPage; +pub(crate) use thread_memory::ThreadMemoryRow; pub(crate) use thread_metadata::ThreadRow; pub(crate) use thread_metadata::anchor_from_item; pub(crate) use thread_metadata::datetime_to_epoch_seconds; diff --git a/codex-rs/state/src/model/thread_memory.rs b/codex-rs/state/src/model/thread_memory.rs new file mode 100644 index 0000000000..6e3a34c21d --- /dev/null +++ b/codex-rs/state/src/model/thread_memory.rs @@ -0,0 +1,52 @@ +use anyhow::Result; +use chrono::DateTime; +use chrono::Utc; +use codex_protocol::ThreadId; +use sqlx::Row; +use sqlx::sqlite::SqliteRow; + +/// Stored memory summaries for a single thread. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ThreadMemory { + pub thread_id: ThreadId, + pub trace_summary: String, + pub memory_summary: String, + pub updated_at: DateTime, +} + +#[derive(Debug)] +pub(crate) struct ThreadMemoryRow { + thread_id: String, + trace_summary: String, + memory_summary: String, + updated_at: i64, +} + +impl ThreadMemoryRow { + pub(crate) fn try_from_row(row: &SqliteRow) -> Result { + Ok(Self { + thread_id: row.try_get("thread_id")?, + trace_summary: row.try_get("trace_summary")?, + memory_summary: row.try_get("memory_summary")?, + updated_at: row.try_get("updated_at")?, + }) + } +} + +impl TryFrom for ThreadMemory { + type Error = anyhow::Error; + + fn try_from(row: ThreadMemoryRow) -> std::result::Result { + Ok(Self { + thread_id: ThreadId::try_from(row.thread_id)?, + trace_summary: row.trace_summary, + memory_summary: row.memory_summary, + updated_at: epoch_seconds_to_datetime(row.updated_at)?, + }) + } +} + +fn epoch_seconds_to_datetime(secs: i64) -> Result> { + DateTime::::from_timestamp(secs, 0) + .ok_or_else(|| anyhow::anyhow!("invalid unix timestamp: {secs}")) +} diff --git a/codex-rs/state/src/model/thread_metadata.rs b/codex-rs/state/src/model/thread_metadata.rs index 7d475efffc..2577ead502 100644 --- a/codex-rs/state/src/model/thread_metadata.rs +++ b/codex-rs/state/src/model/thread_metadata.rs @@ -66,6 +66,8 @@ pub struct ThreadMetadata { pub model_provider: String, /// The working directory for the thread. pub cwd: PathBuf, + /// Version of the CLI that created the thread. + pub cli_version: String, /// A best-effort thread title. pub title: String, /// The sandbox policy (stringified enum). @@ -74,8 +76,8 @@ pub struct ThreadMetadata { pub approval_mode: String, /// The last observed token usage. pub tokens_used: i64, - /// Whether the thread has observed a user message. - pub has_user_event: bool, + /// First user message observed for this thread, if any. + pub first_user_message: Option, /// The archive timestamp, if the thread is archived. pub archived_at: Option>, /// The git commit SHA, if known. @@ -103,6 +105,8 @@ pub struct ThreadMetadataBuilder { pub model_provider: Option, /// The working directory for the thread. pub cwd: PathBuf, + /// Version of the CLI that created the thread. + pub cli_version: Option, /// The sandbox policy. pub sandbox_policy: SandboxPolicy, /// The approval mode. @@ -133,6 +137,7 @@ impl ThreadMetadataBuilder { source, model_provider: None, cwd: PathBuf::new(), + cli_version: None, sandbox_policy: SandboxPolicy::ReadOnly, approval_mode: AskForApproval::OnRequest, archived_at: None, @@ -163,11 +168,12 @@ impl ThreadMetadataBuilder { .clone() .unwrap_or_else(|| default_provider.to_string()), cwd: self.cwd.clone(), + cli_version: self.cli_version.clone().unwrap_or_default(), title: String::new(), sandbox_policy, approval_mode, tokens_used: 0, - has_user_event: false, + first_user_message: None, archived_at: self.archived_at.map(canonicalize_datetime), git_sha: self.git_sha.clone(), git_branch: self.git_branch.clone(), @@ -201,6 +207,9 @@ impl ThreadMetadata { if self.cwd != other.cwd { diffs.push("cwd"); } + if self.cli_version != other.cli_version { + diffs.push("cli_version"); + } if self.title != other.title { diffs.push("title"); } @@ -213,8 +222,8 @@ impl ThreadMetadata { if self.tokens_used != other.tokens_used { diffs.push("tokens_used"); } - if self.has_user_event != other.has_user_event { - diffs.push("has_user_event"); + if self.first_user_message != other.first_user_message { + diffs.push("first_user_message"); } if self.archived_at != other.archived_at { diffs.push("archived_at"); @@ -245,11 +254,12 @@ pub(crate) struct ThreadRow { source: String, model_provider: String, cwd: String, + cli_version: String, title: String, sandbox_policy: String, approval_mode: String, tokens_used: i64, - has_user_event: bool, + first_user_message: String, archived_at: Option, git_sha: Option, git_branch: Option, @@ -266,11 +276,12 @@ impl ThreadRow { source: row.try_get("source")?, model_provider: row.try_get("model_provider")?, cwd: row.try_get("cwd")?, + cli_version: row.try_get("cli_version")?, title: row.try_get("title")?, sandbox_policy: row.try_get("sandbox_policy")?, approval_mode: row.try_get("approval_mode")?, tokens_used: row.try_get("tokens_used")?, - has_user_event: row.try_get("has_user_event")?, + first_user_message: row.try_get("first_user_message")?, archived_at: row.try_get("archived_at")?, git_sha: row.try_get("git_sha")?, git_branch: row.try_get("git_branch")?, @@ -291,11 +302,12 @@ impl TryFrom for ThreadMetadata { source, model_provider, cwd, + cli_version, title, sandbox_policy, approval_mode, tokens_used, - has_user_event, + first_user_message, archived_at, git_sha, git_branch, @@ -309,11 +321,12 @@ impl TryFrom for ThreadMetadata { source, model_provider, cwd: PathBuf::from(cwd), + cli_version, title, sandbox_policy, approval_mode, tokens_used, - has_user_event, + first_user_message: (!first_user_message.is_empty()).then_some(first_user_message), archived_at: archived_at.map(epoch_seconds_to_datetime).transpose()?, git_sha, git_branch, diff --git a/codex-rs/state/src/runtime.rs b/codex-rs/state/src/runtime.rs index 9a750f1d5d..33ee7c844b 100644 --- a/codex-rs/state/src/runtime.rs +++ b/codex-rs/state/src/runtime.rs @@ -3,11 +3,13 @@ use crate::LogEntry; use crate::LogQuery; use crate::LogRow; use crate::SortKey; +use crate::ThreadMemory; use crate::ThreadMetadata; use crate::ThreadMetadataBuilder; use crate::ThreadsPage; use crate::apply_rollout_item; use crate::migrations::MIGRATOR; +use crate::model::ThreadMemoryRow; use crate::model::ThreadRow; use crate::model::anchor_from_item; use crate::model::datetime_to_epoch_seconds; @@ -35,7 +37,8 @@ use std::sync::Arc; use std::time::Duration; use tracing::warn; -pub const STATE_DB_FILENAME: &str = "state.sqlite"; +pub const STATE_DB_FILENAME: &str = "state"; +pub const STATE_DB_VERSION: u32 = 3; const METRIC_DB_INIT: &str = "codex.db.init"; @@ -56,7 +59,8 @@ impl StateRuntime { otel: Option, ) -> anyhow::Result> { tokio::fs::create_dir_all(&codex_home).await?; - let state_path = codex_home.join(STATE_DB_FILENAME); + remove_legacy_state_files(&codex_home).await; + let state_path = state_db_path(codex_home.as_path()); let existed = tokio::fs::try_exists(&state_path).await.unwrap_or(false); let pool = match open_sqlite(&state_path).await { Ok(db) => Arc::new(db), @@ -87,6 +91,80 @@ impl StateRuntime { self.codex_home.as_path() } + /// Get persisted rollout metadata backfill state. + pub async fn get_backfill_state(&self) -> anyhow::Result { + self.ensure_backfill_state_row().await?; + let row = sqlx::query( + r#" +SELECT status, last_watermark, last_success_at +FROM backfill_state +WHERE id = 1 + "#, + ) + .fetch_one(self.pool.as_ref()) + .await?; + crate::BackfillState::try_from_row(&row) + } + + /// Mark rollout metadata backfill as running. + pub async fn mark_backfill_running(&self) -> anyhow::Result<()> { + self.ensure_backfill_state_row().await?; + sqlx::query( + r#" +UPDATE backfill_state +SET status = ?, updated_at = ? +WHERE id = 1 + "#, + ) + .bind(crate::BackfillStatus::Running.as_str()) + .bind(Utc::now().timestamp()) + .execute(self.pool.as_ref()) + .await?; + Ok(()) + } + + /// Persist rollout metadata backfill progress. + pub async fn checkpoint_backfill(&self, watermark: &str) -> anyhow::Result<()> { + self.ensure_backfill_state_row().await?; + sqlx::query( + r#" +UPDATE backfill_state +SET status = ?, last_watermark = ?, updated_at = ? +WHERE id = 1 + "#, + ) + .bind(crate::BackfillStatus::Running.as_str()) + .bind(watermark) + .bind(Utc::now().timestamp()) + .execute(self.pool.as_ref()) + .await?; + Ok(()) + } + + /// Mark rollout metadata backfill as complete. + pub async fn mark_backfill_complete(&self, last_watermark: Option<&str>) -> anyhow::Result<()> { + self.ensure_backfill_state_row().await?; + let now = Utc::now().timestamp(); + sqlx::query( + r#" +UPDATE backfill_state +SET + status = ?, + last_watermark = COALESCE(?, last_watermark), + last_success_at = ?, + updated_at = ? +WHERE id = 1 + "#, + ) + .bind(crate::BackfillStatus::Complete.as_str()) + .bind(last_watermark) + .bind(now) + .bind(now) + .execute(self.pool.as_ref()) + .await?; + Ok(()) + } + /// Load thread metadata by id using the underlying database. pub async fn get_thread(&self, id: ThreadId) -> anyhow::Result> { let row = sqlx::query( @@ -99,11 +177,12 @@ SELECT source, model_provider, cwd, + cli_version, title, sandbox_policy, approval_mode, tokens_used, - has_user_event, + first_user_message, archived_at, git_sha, git_branch, @@ -151,6 +230,26 @@ ORDER BY position ASC Ok(Some(tools)) } + /// Get memory summaries for a thread, if present. + pub async fn get_thread_memory( + &self, + thread_id: ThreadId, + ) -> anyhow::Result> { + let row = sqlx::query( + r#" +SELECT thread_id, trace_summary, memory_summary, updated_at +FROM thread_memory +WHERE thread_id = ? + "#, + ) + .bind(thread_id.to_string()) + .fetch_optional(self.pool.as_ref()) + .await?; + + row.map(|row| ThreadMemoryRow::try_from_row(&row).and_then(ThreadMemory::try_from)) + .transpose() + } + /// Find a rollout path by thread id using the underlying database. pub async fn find_rollout_path_by_id( &self, @@ -197,11 +296,12 @@ SELECT source, model_provider, cwd, + cli_version, title, sandbox_policy, approval_mode, tokens_used, - has_user_event, + first_user_message, archived_at, git_sha, git_branch, @@ -351,17 +451,18 @@ INSERT INTO threads ( source, model_provider, cwd, + cli_version, title, sandbox_policy, approval_mode, tokens_used, - has_user_event, + first_user_message, archived, archived_at, git_sha, git_branch, git_origin_url -) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET rollout_path = excluded.rollout_path, created_at = excluded.created_at, @@ -369,11 +470,12 @@ ON CONFLICT(id) DO UPDATE SET source = excluded.source, model_provider = excluded.model_provider, cwd = excluded.cwd, + cli_version = excluded.cli_version, title = excluded.title, sandbox_policy = excluded.sandbox_policy, approval_mode = excluded.approval_mode, tokens_used = excluded.tokens_used, - has_user_event = excluded.has_user_event, + first_user_message = excluded.first_user_message, archived = excluded.archived, archived_at = excluded.archived_at, git_sha = excluded.git_sha, @@ -388,11 +490,12 @@ ON CONFLICT(id) DO UPDATE SET .bind(metadata.source.as_str()) .bind(metadata.model_provider.as_str()) .bind(metadata.cwd.display().to_string()) + .bind(metadata.cli_version.as_str()) .bind(metadata.title.as_str()) .bind(metadata.sandbox_policy.as_str()) .bind(metadata.approval_mode.as_str()) .bind(metadata.tokens_used) - .bind(metadata.has_user_event) + .bind(metadata.first_user_message.as_deref().unwrap_or_default()) .bind(metadata.archived_at.is_some()) .bind(metadata.archived_at.map(datetime_to_epoch_seconds)) .bind(metadata.git_sha.as_deref()) @@ -403,6 +506,83 @@ ON CONFLICT(id) DO UPDATE SET Ok(()) } + /// Insert or update memory summaries for a thread. + /// + /// This method always advances `updated_at`, even if summaries are unchanged. + pub async fn upsert_thread_memory( + &self, + thread_id: ThreadId, + trace_summary: &str, + memory_summary: &str, + ) -> anyhow::Result { + if self.get_thread(thread_id).await?.is_none() { + return Err(anyhow::anyhow!("thread not found: {thread_id}")); + } + + let updated_at = Utc::now().timestamp(); + sqlx::query( + r#" +INSERT INTO thread_memory ( + thread_id, + trace_summary, + memory_summary, + updated_at +) VALUES (?, ?, ?, ?) +ON CONFLICT(thread_id) DO UPDATE SET + trace_summary = excluded.trace_summary, + memory_summary = excluded.memory_summary, + updated_at = CASE + WHEN excluded.updated_at <= thread_memory.updated_at THEN thread_memory.updated_at + 1 + ELSE excluded.updated_at + END + "#, + ) + .bind(thread_id.to_string()) + .bind(trace_summary) + .bind(memory_summary) + .bind(updated_at) + .execute(self.pool.as_ref()) + .await?; + + self.get_thread_memory(thread_id) + .await? + .ok_or_else(|| anyhow::anyhow!("failed to load upserted thread memory: {thread_id}")) + } + + /// Get the last `n` memories for threads with an exact cwd match. + pub async fn get_last_n_thread_memories_for_cwd( + &self, + cwd: &Path, + n: usize, + ) -> anyhow::Result> { + if n == 0 { + return Ok(Vec::new()); + } + + let rows = sqlx::query( + r#" +SELECT + m.thread_id, + m.trace_summary, + m.memory_summary, + m.updated_at +FROM thread_memory AS m +INNER JOIN threads AS t ON t.id = m.thread_id +WHERE t.cwd = ? +ORDER BY m.updated_at DESC, m.thread_id DESC +LIMIT ? + "#, + ) + .bind(cwd.display().to_string()) + .bind(n as i64) + .fetch_all(self.pool.as_ref()) + .await?; + + rows.into_iter() + .map(|row| ThreadMemoryRow::try_from_row(&row).and_then(ThreadMemory::try_from)) + .collect() + } + /// Persist dynamic tools for a thread if none have been stored yet. /// /// Dynamic tools are defined at thread start and should not change afterward. @@ -419,6 +599,7 @@ ON CONFLICT(id) DO UPDATE SET return Ok(()); } let thread_id = thread_id.to_string(); + let mut tx = self.pool.begin().await?; for (idx, tool) in tools.iter().enumerate() { let position = i64::try_from(idx).unwrap_or(i64::MAX); let input_schema = serde_json::to_string(&tool.input_schema)?; @@ -439,9 +620,10 @@ ON CONFLICT(thread_id, position) DO NOTHING .bind(tool.name.as_str()) .bind(tool.description.as_str()) .bind(input_schema) - .execute(self.pool.as_ref()) + .execute(&mut *tx) .await?; } + tx.commit().await?; Ok(()) } @@ -534,6 +716,22 @@ ON CONFLICT(thread_id, position) DO NOTHING } self.upsert_thread(&metadata).await } + + async fn ensure_backfill_state_row(&self) -> anyhow::Result<()> { + sqlx::query( + r#" +INSERT INTO backfill_state (id, status, last_watermark, last_success_at, updated_at) +VALUES (?, ?, NULL, NULL, ?) +ON CONFLICT(id) DO NOTHING + "#, + ) + .bind(1_i64) + .bind(crate::BackfillStatus::Pending.as_str()) + .bind(Utc::now().timestamp()) + .execute(self.pool.as_ref()) + .await?; + Ok(()) + } } fn push_log_filters<'a>(builder: &mut QueryBuilder<'a, Sqlite>, query: &'a LogQuery) { @@ -622,6 +820,77 @@ async fn open_sqlite(path: &Path) -> anyhow::Result { Ok(pool) } +pub fn state_db_filename() -> String { + format!("{STATE_DB_FILENAME}_{STATE_DB_VERSION}.sqlite") +} + +pub fn state_db_path(codex_home: &Path) -> PathBuf { + codex_home.join(state_db_filename()) +} + +async fn remove_legacy_state_files(codex_home: &Path) { + let current_name = state_db_filename(); + let mut entries = match tokio::fs::read_dir(codex_home).await { + Ok(entries) => entries, + Err(err) => { + warn!( + "failed to read codex_home for state db cleanup {}: {err}", + codex_home.display() + ); + return; + } + }; + while let Ok(Some(entry)) = entries.next_entry().await { + if !entry + .file_type() + .await + .map(|file_type| file_type.is_file()) + .unwrap_or(false) + { + continue; + } + let file_name = entry.file_name(); + let file_name = file_name.to_string_lossy(); + if !should_remove_state_file(file_name.as_ref(), current_name.as_str()) { + continue; + } + + let legacy_path = entry.path(); + if let Err(err) = tokio::fs::remove_file(&legacy_path).await { + warn!( + "failed to remove legacy state db file {}: {err}", + legacy_path.display() + ); + } + } +} + +fn should_remove_state_file(file_name: &str, current_name: &str) -> bool { + let mut base_name = file_name; + for suffix in ["-wal", "-shm", "-journal"] { + if let Some(stripped) = file_name.strip_suffix(suffix) { + base_name = stripped; + break; + } + } + if base_name == current_name { + return false; + } + let unversioned_name = format!("{STATE_DB_FILENAME}.sqlite"); + if base_name == unversioned_name { + return true; + } + + let Some(version_with_extension) = base_name.strip_prefix(&format!("{STATE_DB_FILENAME}_")) + else { + return false; + }; + let Some(version_suffix) = version_with_extension.strip_suffix(".sqlite") else { + return false; + }; + !version_suffix.is_empty() && version_suffix.chars().all(|ch| ch.is_ascii_digit()) +} + fn push_thread_filters<'a>( builder: &mut QueryBuilder<'a, Sqlite>, archived_only: bool, @@ -636,7 +905,7 @@ fn push_thread_filters<'a>( } else { builder.push(" AND archived = 0"); } - builder.push(" AND has_user_event = 1"); + builder.push(" AND first_user_message <> ''"); if !allowed_sources.is_empty() { builder.push(" AND source IN ("); let mut separated = builder.separated(", "); @@ -690,3 +959,453 @@ fn push_thread_order_and_limit( builder.push(" LIMIT "); builder.push_bind(limit as i64); } + +#[cfg(test)] +mod tests { + use super::STATE_DB_FILENAME; + use super::STATE_DB_VERSION; + use super::StateRuntime; + use super::ThreadMetadata; + use super::state_db_filename; + use chrono::DateTime; + use chrono::Utc; + use codex_protocol::ThreadId; + use codex_protocol::protocol::AskForApproval; + use codex_protocol::protocol::SandboxPolicy; + use pretty_assertions::assert_eq; + use sqlx::Row; + use std::path::Path; + use std::path::PathBuf; + use std::time::SystemTime; + use std::time::UNIX_EPOCH; + use uuid::Uuid; + + fn unique_temp_dir() -> PathBuf { + let nanos = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_or(0, |duration| duration.as_nanos()); + std::env::temp_dir().join(format!( + "codex-state-runtime-test-{nanos}-{}", + Uuid::new_v4() + )) + } + + #[tokio::test] + async fn init_removes_legacy_state_db_files() { + let codex_home = unique_temp_dir(); + tokio::fs::create_dir_all(&codex_home) + .await + .expect("create codex_home"); + + let current_name = state_db_filename(); + let previous_version = STATE_DB_VERSION.saturating_sub(1); + let unversioned_name = format!("{STATE_DB_FILENAME}.sqlite"); + for suffix in ["", "-wal", "-shm", "-journal"] { + let path = codex_home.join(format!("{unversioned_name}{suffix}")); + tokio::fs::write(path, b"legacy") + .await + .expect("write legacy"); + let old_version_path = codex_home.join(format!( + "{STATE_DB_FILENAME}_{previous_version}.sqlite{suffix}" + )); + tokio::fs::write(old_version_path, b"old_version") + .await + .expect("write old version"); + } + let unrelated_path = codex_home.join("state.sqlite_backup"); + tokio::fs::write(&unrelated_path, b"keep") + .await + .expect("write unrelated"); + let numeric_path = codex_home.join("123"); + tokio::fs::write(&numeric_path, b"keep") + .await + .expect("write numeric"); + + let _runtime = StateRuntime::init(codex_home.clone(), "test-provider".to_string(), None) + .await + .expect("initialize runtime"); + + for suffix in ["", "-wal", "-shm", "-journal"] { + let legacy_path = codex_home.join(format!("{unversioned_name}{suffix}")); + assert_eq!( + tokio::fs::try_exists(&legacy_path) + .await + .expect("check legacy path"), + false + ); + let old_version_path = codex_home.join(format!( + "{STATE_DB_FILENAME}_{previous_version}.sqlite{suffix}" + )); + assert_eq!( + tokio::fs::try_exists(&old_version_path) + .await + .expect("check old version path"), + false + ); + } + assert_eq!( + tokio::fs::try_exists(codex_home.join(current_name)) + .await + .expect("check new db path"), + true + ); + assert_eq!( + tokio::fs::try_exists(&unrelated_path) + .await + .expect("check unrelated path"), + true + ); + assert_eq!( + tokio::fs::try_exists(&numeric_path) + .await + .expect("check numeric path"), + true + ); + + let _ = tokio::fs::remove_dir_all(codex_home).await; + } + + #[tokio::test] + async fn backfill_state_persists_progress_and_completion() { + let codex_home = unique_temp_dir(); + let runtime = StateRuntime::init(codex_home.clone(), "test-provider".to_string(), None) + .await + .expect("initialize runtime"); + + let initial = runtime + .get_backfill_state() + .await + .expect("get initial backfill state"); + assert_eq!(initial.status, crate::BackfillStatus::Pending); + assert_eq!(initial.last_watermark, None); + assert_eq!(initial.last_success_at, None); + + runtime + .mark_backfill_running() + .await + .expect("mark backfill running"); + runtime + .checkpoint_backfill("sessions/2026/01/27/rollout-a.jsonl") + .await + .expect("checkpoint backfill"); + + let running = runtime + .get_backfill_state() + .await + .expect("get running backfill state"); + assert_eq!(running.status, crate::BackfillStatus::Running); + assert_eq!( + running.last_watermark, + Some("sessions/2026/01/27/rollout-a.jsonl".to_string()) + ); + assert_eq!(running.last_success_at, None); + + runtime + .mark_backfill_complete(Some("sessions/2026/01/28/rollout-b.jsonl")) + .await + .expect("mark backfill complete"); + let completed = runtime + .get_backfill_state() + .await + .expect("get completed backfill state"); + assert_eq!(completed.status, crate::BackfillStatus::Complete); + assert_eq!( + completed.last_watermark, + Some("sessions/2026/01/28/rollout-b.jsonl".to_string()) + ); + assert!(completed.last_success_at.is_some()); + + let _ = tokio::fs::remove_dir_all(codex_home).await; + } + + #[tokio::test] + async fn upsert_and_get_thread_memory() { + let codex_home = unique_temp_dir(); + let runtime = StateRuntime::init(codex_home.clone(), "test-provider".to_string(), None) + .await + .expect("initialize runtime"); + + let thread_id = ThreadId::from_string(&Uuid::new_v4().to_string()).expect("thread id"); + let metadata = test_thread_metadata(&codex_home, thread_id, codex_home.join("a")); + runtime + .upsert_thread(&metadata) + .await + .expect("upsert thread"); + + assert_eq!( + runtime + .get_thread_memory(thread_id) + .await + .expect("get memory before insert"), + None + ); + + let inserted = runtime + .upsert_thread_memory(thread_id, "trace one", "memory one") + .await + .expect("upsert memory"); + assert_eq!(inserted.thread_id, thread_id); + assert_eq!(inserted.trace_summary, "trace one"); + assert_eq!(inserted.memory_summary, "memory one"); + + let updated = runtime + .upsert_thread_memory(thread_id, "trace two", "memory two") + .await + .expect("update memory"); + assert_eq!(updated.thread_id, thread_id); + assert_eq!(updated.trace_summary, "trace two"); + assert_eq!(updated.memory_summary, "memory two"); + assert!( + updated.updated_at >= inserted.updated_at, + "updated_at should not move backward" + ); + + let _ = tokio::fs::remove_dir_all(codex_home).await; + } + + #[tokio::test] + async fn get_last_n_thread_memories_for_cwd_matches_exactly() { + let codex_home = unique_temp_dir(); + let runtime = StateRuntime::init(codex_home.clone(), "test-provider".to_string(), None) + .await + .expect("initialize runtime"); + + let cwd_a = codex_home.join("workspace-a"); + let cwd_b = codex_home.join("workspace-b"); + let t1 = ThreadId::from_string(&Uuid::new_v4().to_string()).expect("thread id"); + let t2 = ThreadId::from_string(&Uuid::new_v4().to_string()).expect("thread id"); + let t3 = ThreadId::from_string(&Uuid::new_v4().to_string()).expect("thread id"); + runtime + .upsert_thread(&test_thread_metadata(&codex_home, t1, cwd_a.clone())) + .await + .expect("upsert thread t1"); + runtime + .upsert_thread(&test_thread_metadata(&codex_home, t2, cwd_a.clone())) + .await + .expect("upsert thread t2"); + runtime + .upsert_thread(&test_thread_metadata(&codex_home, t3, cwd_b.clone())) + .await + .expect("upsert thread t3"); + + let first = runtime + .upsert_thread_memory(t1, "trace-1", "memory-1") + .await + .expect("upsert t1 memory"); + runtime + .upsert_thread_memory(t2, "trace-2", "memory-2") + .await + .expect("upsert t2 memory"); + runtime + .upsert_thread_memory(t3, "trace-3", "memory-3") + .await + .expect("upsert t3 memory"); + // Ensure deterministic ordering even when updates happen in the same second. + runtime + .upsert_thread_memory(t1, "trace-1b", "memory-1b") + .await + .expect("upsert t1 memory again"); + + let cwd_a_memories = runtime + .get_last_n_thread_memories_for_cwd(cwd_a.as_path(), 2) + .await + .expect("list cwd a memories"); + assert_eq!(cwd_a_memories.len(), 2); + assert_eq!(cwd_a_memories[0].thread_id, t1); + assert_eq!(cwd_a_memories[0].trace_summary, "trace-1b"); + assert_eq!(cwd_a_memories[0].memory_summary, "memory-1b"); + assert_eq!(cwd_a_memories[1].thread_id, t2); + assert!(cwd_a_memories[0].updated_at >= first.updated_at); + + let cwd_b_memories = runtime + .get_last_n_thread_memories_for_cwd(cwd_b.as_path(), 10) + .await + .expect("list cwd b memories"); + assert_eq!(cwd_b_memories.len(), 1); + assert_eq!(cwd_b_memories[0].thread_id, t3); + + let none = runtime + .get_last_n_thread_memories_for_cwd(codex_home.join("missing").as_path(), 10) + .await + .expect("list missing cwd memories"); + assert_eq!(none, Vec::new()); + + let _ = tokio::fs::remove_dir_all(codex_home).await; + } + + #[tokio::test] + async fn upsert_thread_memory_errors_for_unknown_thread() { + let codex_home = unique_temp_dir(); + let runtime = StateRuntime::init(codex_home.clone(), "test-provider".to_string(), None) + .await + .expect("initialize runtime"); + + let unknown_thread_id = + ThreadId::from_string(&Uuid::new_v4().to_string()).expect("thread id"); + let err = runtime + .upsert_thread_memory(unknown_thread_id, "trace", "memory") + .await + .expect_err("unknown thread should fail"); + assert!( + err.to_string().contains("thread not found"), + "error should mention missing thread: {err}" + ); + + let _ = tokio::fs::remove_dir_all(codex_home).await; + } + + #[tokio::test] + async fn get_last_n_thread_memories_for_cwd_zero_returns_empty() { + let codex_home = unique_temp_dir(); + let runtime = StateRuntime::init(codex_home.clone(), "test-provider".to_string(), None) + .await + .expect("initialize runtime"); + + let thread_id = ThreadId::from_string(&Uuid::new_v4().to_string()).expect("thread id"); + let cwd = codex_home.join("workspace"); + runtime + .upsert_thread(&test_thread_metadata(&codex_home, thread_id, cwd.clone())) + .await + .expect("upsert thread"); + runtime + .upsert_thread_memory(thread_id, "trace", "memory") + .await + .expect("upsert memory"); + + let memories = runtime + .get_last_n_thread_memories_for_cwd(cwd.as_path(), 0) + .await + .expect("query memories"); + assert_eq!(memories, Vec::new()); + + let _ = tokio::fs::remove_dir_all(codex_home).await; + } + + #[tokio::test] + async fn get_last_n_thread_memories_for_cwd_does_not_prefix_match() { + let codex_home = unique_temp_dir(); + let runtime = StateRuntime::init(codex_home.clone(), "test-provider".to_string(), None) + .await + .expect("initialize runtime"); + + let cwd_exact = codex_home.join("workspace"); + let cwd_prefix = codex_home.join("workspace-child"); + let t_exact = ThreadId::from_string(&Uuid::new_v4().to_string()).expect("thread id"); + let t_prefix = ThreadId::from_string(&Uuid::new_v4().to_string()).expect("thread id"); + runtime + .upsert_thread(&test_thread_metadata( + &codex_home, + t_exact, + cwd_exact.clone(), + )) + .await + .expect("upsert exact thread"); + runtime + .upsert_thread(&test_thread_metadata( + &codex_home, + t_prefix, + cwd_prefix.clone(), + )) + .await + .expect("upsert prefix thread"); + runtime + .upsert_thread_memory(t_exact, "trace-exact", "memory-exact") + .await + .expect("upsert exact memory"); + runtime + .upsert_thread_memory(t_prefix, "trace-prefix", "memory-prefix") + .await + .expect("upsert prefix memory"); + + let exact_only = runtime + .get_last_n_thread_memories_for_cwd(cwd_exact.as_path(), 10) + .await + .expect("query exact cwd"); + assert_eq!(exact_only.len(), 1); + assert_eq!(exact_only[0].thread_id, t_exact); + assert_eq!(exact_only[0].memory_summary, "memory-exact"); + + let _ = tokio::fs::remove_dir_all(codex_home).await; + } + + #[tokio::test] + async fn deleting_thread_cascades_thread_memory() { + let codex_home = unique_temp_dir(); + let runtime = StateRuntime::init(codex_home.clone(), "test-provider".to_string(), None) + .await + .expect("initialize runtime"); + + let thread_id = ThreadId::from_string(&Uuid::new_v4().to_string()).expect("thread id"); + let cwd = codex_home.join("workspace"); + runtime + .upsert_thread(&test_thread_metadata(&codex_home, thread_id, cwd)) + .await + .expect("upsert thread"); + runtime + .upsert_thread_memory(thread_id, "trace", "memory") + .await + .expect("upsert memory"); + + let count_before = + sqlx::query("SELECT COUNT(*) AS count FROM thread_memory WHERE thread_id = ?") + .bind(thread_id.to_string()) + .fetch_one(runtime.pool.as_ref()) + .await + .expect("count before delete") + .try_get::("count") + .expect("count value"); + assert_eq!(count_before, 1); + + sqlx::query("DELETE FROM threads WHERE id = ?") + .bind(thread_id.to_string()) + .execute(runtime.pool.as_ref()) + .await + .expect("delete thread"); + + let count_after = + sqlx::query("SELECT COUNT(*) AS count FROM thread_memory WHERE thread_id = ?") + .bind(thread_id.to_string()) + .fetch_one(runtime.pool.as_ref()) + .await + .expect("count after delete") + .try_get::("count") + .expect("count value"); + assert_eq!(count_after, 0); + assert_eq!( + runtime + .get_thread_memory(thread_id) + .await + .expect("get memory after delete"), + None + ); + + let _ = tokio::fs::remove_dir_all(codex_home).await; + } + + fn test_thread_metadata( + codex_home: &Path, + thread_id: ThreadId, + cwd: PathBuf, + ) -> ThreadMetadata { + let now = DateTime::::from_timestamp(1_700_000_000, 0).expect("timestamp"); + ThreadMetadata { + id: thread_id, + rollout_path: codex_home.join(format!("rollout-{thread_id}.jsonl")), + created_at: now, + updated_at: now, + source: "cli".to_string(), + model_provider: "test-provider".to_string(), + cwd, + cli_version: "0.0.0".to_string(), + title: String::new(), + sandbox_policy: crate::extract::enum_to_string(&SandboxPolicy::ReadOnly), + approval_mode: crate::extract::enum_to_string(&AskForApproval::OnRequest), + tokens_used: 0, + first_user_message: Some("hello".to_string()), + archived_at: None, + git_sha: None, + git_branch: None, + git_origin_url: None, + } + } +} diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 0160e8f23d..718615237d 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -60,6 +60,7 @@ use codex_core::protocol::TokenUsage; #[cfg(target_os = "windows")] use codex_core::windows_sandbox::WindowsSandboxLevelExt; use codex_otel::OtelManager; +use codex_otel::TelemetryAuthMode; use codex_protocol::ThreadId; use codex_protocol::config_types::Personality; #[cfg(target_os = "windows")] @@ -102,6 +103,11 @@ use toml::Value as TomlValue; const EXTERNAL_EDITOR_HINT: &str = "Save and close external editor to continue."; const THREAD_EVENT_CHANNEL_CAPACITY: usize = 32768; +/// Baseline cadence for periodic stream commit animation ticks. +/// +/// Smooth-mode streaming drains one line per tick, so this interval controls +/// perceived typing speed for non-backlogged output. +const COMMIT_ANIMATION_TICK: Duration = tui::TARGET_FRAME_INTERVAL; #[derive(Debug, Clone)] pub struct AppExitInfo { @@ -530,6 +536,8 @@ pub(crate) struct App { /// Controls the animation thread that sends CommitTick events. pub(crate) commit_anim_running: Arc, + // Shared across ChatWidget instances so invalid status-line config warnings only emit once. + status_line_invalid_items_warned: Arc, // Esc-backtracking state grouped pub(crate) backtrack: crate::app_backtrack::BacktrackState, @@ -600,6 +608,7 @@ impl App { is_first_run: false, feedback_audience: self.feedback_audience, model: Some(self.chat_widget.current_model().to_string()), + status_line_invalid_items_warned: self.status_line_invalid_items_warned.clone(), otel_manager: self.otel_manager.clone(), } } @@ -901,6 +910,7 @@ impl App { for event in snapshot.events { self.handle_codex_event_replay(event); } + self.refresh_status_line(); } #[allow(clippy::too_many_arguments)] @@ -966,17 +976,29 @@ impl App { } else { FeedbackAudience::External }; + let auth_mode = auth_ref + .map(CodexAuth::auth_mode) + .map(TelemetryAuthMode::from); let otel_manager = OtelManager::new( ThreadId::new(), model.as_str(), model.as_str(), auth_ref.and_then(CodexAuth::get_account_id), auth_ref.and_then(CodexAuth::get_account_email), - auth_ref.map(CodexAuth::api_auth_mode), + auth_mode, config.otel.log_user_prompt, codex_core::terminal::user_agent(), SessionSource::Cli, ); + if config + .tui_status_line + .as_ref() + .is_some_and(|cmd| !cmd.is_empty()) + { + otel_manager.counter("codex.status_line", 1, &[]); + } + + let status_line_invalid_items_warned = Arc::new(AtomicBool::new(false)); let enhanced_keys_supported = tui.enhanced_keys_supported(); let mut chat_widget = match session_selection { @@ -998,6 +1020,7 @@ impl App { is_first_run, feedback_audience, model: Some(model.clone()), + status_line_invalid_items_warned: status_line_invalid_items_warned.clone(), otel_manager: otel_manager.clone(), }; ChatWidget::new(init, thread_manager.clone()) @@ -1027,11 +1050,13 @@ impl App { is_first_run, feedback_audience, model: config.model.clone(), + status_line_invalid_items_warned: status_line_invalid_items_warned.clone(), otel_manager: otel_manager.clone(), }; ChatWidget::new_from_existing(init, resumed.thread, resumed.session_configured) } SessionSelection::Fork(path) => { + otel_manager.counter("codex.thread.fork", 1, &[("source", "cli_subcommand")]); let forked = thread_manager .fork_thread(usize::MAX, config.clone(), path.clone()) .await @@ -1056,6 +1081,7 @@ impl App { is_first_run, feedback_audience, model: config.model.clone(), + status_line_invalid_items_warned: status_line_invalid_items_warned.clone(), otel_manager: otel_manager.clone(), }; ChatWidget::new_from_existing(init, forked.thread, forked.session_configured) @@ -1087,6 +1113,7 @@ impl App { deferred_history_lines: Vec::new(), has_emitted_history_lines: false, commit_anim_running: Arc::new(AtomicBool::new(false)), + status_line_invalid_items_warned: status_line_invalid_items_warned.clone(), backtrack: BacktrackState::default(), backtrack_render_pending: false, feedback: feedback.clone(), @@ -1215,6 +1242,13 @@ impl App { tui: &mut tui::Tui, event: TuiEvent, ) -> Result { + if matches!(event, TuiEvent::Draw) { + let size = tui.terminal.size()?; + if size != tui.terminal.last_known_screen_size { + self.refresh_status_line(); + } + } + if self.overlay.is_some() { let _ = self.handle_backtrack_overlay_event(tui, event).await?; } else { @@ -1288,6 +1322,7 @@ impl App { is_first_run: false, feedback_audience: self.feedback_audience, model: Some(model), + status_line_invalid_items_warned: self.status_line_invalid_items_warned.clone(), otel_manager: self.otel_manager.clone(), }; self.chat_widget = ChatWidget::new(init, self.server.clone()); @@ -1399,11 +1434,15 @@ impl App { tui.frame_requester().schedule_frame(); } AppEvent::ForkCurrentSession => { + self.otel_manager + .counter("codex.thread.fork", 1, &[("source", "slash_command")]); let summary = session_summary( self.chat_widget.token_usage(), self.chat_widget.thread_id(), self.chat_widget.thread_name(), ); + self.chat_widget + .add_plain_history_lines(vec!["/fork".magenta().into()]); if let Some(path) = self.chat_widget.rollout_path() { match self .server @@ -1485,7 +1524,7 @@ impl App { let running = self.commit_anim_running.clone(); thread::spawn(move || { while running.load(Ordering::Relaxed) { - thread::sleep(Duration::from_millis(50)); + thread::sleep(COMMIT_ANIMATION_TICK); tx.send(AppEvent::CommitTick); } }); @@ -1557,12 +1596,15 @@ impl App { } AppEvent::UpdateReasoningEffort(effort) => { self.on_update_reasoning_effort(effort); + self.refresh_status_line(); } AppEvent::UpdateModel(model) => { self.chat_widget.set_model(&model); + self.refresh_status_line(); } AppEvent::UpdateCollaborationMode(mask) => { self.chat_widget.set_collaboration_mask(mask); + self.refresh_status_line(); } AppEvent::UpdatePersonality(personality) => { self.on_update_personality(personality); @@ -2196,11 +2238,45 @@ impl App { )); } }, + AppEvent::StatusLineSetup { items } => { + let ids = items.iter().map(ToString::to_string).collect::>(); + let edit = codex_core::config::edit::status_line_items_edit(&ids); + let apply_result = ConfigEditsBuilder::new(&self.config.codex_home) + .with_edits([edit]) + .apply() + .await; + match apply_result { + Ok(()) => { + self.config.tui_status_line = if ids.is_empty() { + None + } else { + Some(ids.clone()) + }; + self.chat_widget.setup_status_line(items); + } + Err(err) => { + tracing::error!(error = %err, "failed to persist status line items; keeping previous selection"); + self.chat_widget + .add_error_message(format!("Failed to save status line items: {err}")); + } + } + } + AppEvent::StatusLineBranchUpdated { cwd, branch } => { + self.chat_widget.set_status_line_branch(cwd, branch); + self.refresh_status_line(); + } + AppEvent::StatusLineSetupCancelled => { + self.chat_widget.cancel_status_line_setup(); + } } Ok(AppRunControl::Continue) } fn handle_codex_event_now(&mut self, event: Event) { + let needs_refresh = matches!( + event.msg, + EventMsg::SessionConfigured(_) | EventMsg::TokenCount(_) + ); if self.suppress_shutdown_complete && matches!(event.msg, EventMsg::ShutdownComplete) { self.suppress_shutdown_complete = false; return; @@ -2212,6 +2288,10 @@ impl App { } self.handle_backtrack_event(&event.msg); self.chat_widget.handle_codex_event(event); + + if needs_refresh { + self.refresh_status_line(); + } } fn handle_codex_event_replay(&mut self, event: Event) { @@ -2321,6 +2401,7 @@ impl App { fn personality_label(personality: Personality) -> &'static str { match personality { + Personality::None => "None", Personality::Friendly => "Friendly", Personality::Pragmatic => "Pragmatic", } @@ -2465,6 +2546,10 @@ impl App { }; } + fn refresh_status_line(&mut self) { + self.chat_widget.refresh_status_line(); + } + #[cfg(target_os = "windows")] fn spawn_world_writable_scan( cwd: PathBuf, @@ -2621,6 +2706,7 @@ mod tests { has_emitted_history_lines: false, enhanced_keys_supported: false, commit_anim_running: Arc::new(AtomicBool::new(false)), + status_line_invalid_items_warned: Arc::new(AtomicBool::new(false)), backtrack: BacktrackState::default(), backtrack_render_pending: false, feedback: codex_feedback::CodexFeedback::new(), @@ -2674,6 +2760,7 @@ mod tests { has_emitted_history_lines: false, enhanced_keys_supported: false, commit_anim_running: Arc::new(AtomicBool::new(false)), + status_line_invalid_items_warned: Arc::new(AtomicBool::new(false)), backtrack: BacktrackState::default(), backtrack_render_pending: false, feedback: codex_feedback::CodexFeedback::new(), diff --git a/codex-rs/tui/src/app_event.rs b/codex-rs/tui/src/app_event.rs index bbd228e114..bd48e5de1b 100644 --- a/codex-rs/tui/src/app_event.rs +++ b/codex-rs/tui/src/app_event.rs @@ -19,6 +19,7 @@ use codex_protocol::ThreadId; use codex_protocol::openai_models::ModelPreset; use crate::bottom_pane::ApprovalRequest; +use crate::bottom_pane::StatusLineItem; use crate::history_cell::HistoryCell; use codex_core::features::Feature; @@ -292,6 +293,18 @@ pub(crate) enum AppEvent { /// Launch the external editor after a normal draw has completed. LaunchExternalEditor, + + /// Async update of the current git branch for status line rendering. + StatusLineBranchUpdated { + cwd: PathBuf, + branch: Option, + }, + /// Apply a user-confirmed status-line item ordering/selection. + StatusLineSetup { + items: Vec, + }, + /// Dismiss the status-line setup UI without changing config. + StatusLineSetupCancelled, } /// The exit strategy requested by the UI layer. diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index 116e9c3abe..4afbfea01a 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -82,9 +82,12 @@ //! edits and renders a placeholder prompt instead of the editable textarea. This is part of the //! overall state machine, since it affects which transitions are even possible from a given UI //! state. +use crate::bottom_pane::footer::mode_indicator_line; +use crate::bottom_pane::selection_popup_common::truncate_line_with_ellipsis_if_overflow; use crate::key_hint; use crate::key_hint::KeyBinding; use crate::key_hint::has_ctrl_or_alt; +use crate::ui_consts::FOOTER_INDENT_COLS; use crossterm::event::KeyCode; use crossterm::event::KeyEvent; use crossterm::event::KeyEventKind; @@ -118,6 +121,7 @@ use super::footer::footer_height; use super::footer::footer_hint_items_width; use super::footer::footer_line_width; use super::footer::inset_footer_hint_area; +use super::footer::max_left_width_for_right; use super::footer::render_context_right; use super::footer::render_footer_from_props; use super::footer::render_footer_hint_items; @@ -295,6 +299,8 @@ pub(crate) struct ChatComposer { connectors_enabled: bool, personality_command_enabled: bool, windows_degraded_sandbox_active: bool, + status_line_value: Option>, + status_line_enabled: bool, } #[derive(Clone, Debug)] @@ -384,6 +390,8 @@ impl ChatComposer { connectors_enabled: false, personality_command_enabled: false, windows_degraded_sandbox_active: false, + status_line_value: None, + status_line_enabled: false, }; // Apply configuration via the setter to keep side-effects centralized. this.set_disable_paste_burst(disable_paste_burst); @@ -458,7 +466,7 @@ impl ChatComposer { let footer_props = self.footer_props(); let footer_hint_height = self .custom_footer_height() - .unwrap_or_else(|| footer_height(footer_props)); + .unwrap_or_else(|| footer_height(&footer_props)); let footer_spacing = Self::footer_spacing(footer_hint_height); let footer_total_height = footer_hint_height + footer_spacing; let popup_constraint = match &self.active_popup { @@ -2524,6 +2532,8 @@ impl ChatComposer { is_wsl, context_window_percent: self.context_window_percent, context_window_used_tokens: self.context_window_used_tokens, + status_line_value: self.status_line_value.clone(), + status_line_enabled: self.status_line_enabled, } } @@ -3006,6 +3016,14 @@ impl ChatComposer { self.footer_mode = reset_mode_after_activity(self.footer_mode); } } + + pub(crate) fn set_status_line(&mut self, status_line: Option>) { + self.status_line_value = status_line; + } + + pub(crate) fn set_status_line_enabled(&mut self, enabled: bool) { + self.status_line_enabled = enabled; + } } fn skill_display_name(skill: &SkillMetadata) -> &str { @@ -3046,7 +3064,7 @@ impl Renderable for ChatComposer { let footer_props = self.footer_props(); let footer_hint_height = self .custom_footer_height() - .unwrap_or_else(|| footer_height(footer_props)); + .unwrap_or_else(|| footer_height(&footer_props)); let footer_spacing = Self::footer_spacing(footer_hint_height); let footer_total_height = footer_hint_height + footer_spacing; const COLS_WITH_MARGIN: u16 = LIVE_PREFIX_COLS + 1; @@ -3099,14 +3117,9 @@ impl ChatComposer { | FooterMode::ShortcutOverlay | FooterMode::EscHint => false, }; - let context_line = context_window_line( - footer_props.context_window_percent, - footer_props.context_window_used_tokens, - ); - let context_width = context_line.width() as u16; let custom_height = self.custom_footer_height(); let footer_hint_height = - custom_height.unwrap_or_else(|| footer_height(footer_props)); + custom_height.unwrap_or_else(|| footer_height(&footer_props)); let footer_spacing = Self::footer_spacing(footer_hint_height); let hint_rect = if footer_spacing > 0 && footer_hint_height > 0 { let [_, hint_rect] = Layout::vertical([ @@ -3118,24 +3131,80 @@ impl ChatComposer { } else { popup_rect }; - let left_width = if self.footer_flash_visible() { + let available_width = + hint_rect.width.saturating_sub(FOOTER_INDENT_COLS as u16) as usize; + let status_line = footer_props + .status_line_value + .as_ref() + .map(|line| line.clone().dim()); + let status_line_candidate = footer_props.status_line_enabled + && matches!( + footer_props.mode, + FooterMode::ComposerEmpty | FooterMode::ComposerHasDraft + ); + let mut truncated_status_line = if status_line_candidate { + status_line.as_ref().map(|line| { + truncate_line_with_ellipsis_if_overflow(line.clone(), available_width) + }) + } else { + None + }; + let status_line_active = status_line_candidate && truncated_status_line.is_some(); + let left_mode_indicator = if status_line_active { + None + } else { + self.collaboration_mode_indicator + }; + let mut left_width = if self.footer_flash_visible() { self.footer_flash .as_ref() .map(|flash| flash.line.width() as u16) .unwrap_or(0) } else if let Some(items) = self.footer_hint_override.as_ref() { footer_hint_items_width(items) + } else if status_line_active { + truncated_status_line + .as_ref() + .map(|line| line.width() as u16) + .unwrap_or(0) } else { footer_line_width( - footer_props, - self.collaboration_mode_indicator, + &footer_props, + left_mode_indicator, show_cycle_hint, show_shortcuts_hint, show_queue_hint, ) }; + let right_line = if status_line_active { + let full = + mode_indicator_line(self.collaboration_mode_indicator, show_cycle_hint); + let compact = mode_indicator_line(self.collaboration_mode_indicator, false); + let full_width = full.as_ref().map(|l| l.width() as u16).unwrap_or(0); + if can_show_left_with_context(hint_rect, left_width, full_width) { + full + } else { + compact + } + } else { + Some(context_window_line( + footer_props.context_window_percent, + footer_props.context_window_used_tokens, + )) + }; + let right_width = right_line.as_ref().map(|l| l.width() as u16).unwrap_or(0); + if status_line_active + && let Some(max_left) = max_left_width_for_right(hint_rect, right_width) + && left_width > max_left + && let Some(line) = status_line.as_ref().map(|line| { + truncate_line_with_ellipsis_if_overflow(line.clone(), max_left as usize) + }) + { + left_width = line.width() as u16; + truncated_status_line = Some(line); + } let can_show_left_and_context = - can_show_left_with_context(hint_rect, left_width, context_width); + can_show_left_with_context(hint_rect, left_width, right_width); let has_override = self.footer_flash_visible() || self.footer_hint_override.is_some(); let single_line_layout = if has_override { @@ -3149,8 +3218,8 @@ impl ChatComposer { // the context indicator on narrow widths. Some(single_line_footer_layout( hint_rect, - context_width, - self.collaboration_mode_indicator, + right_width, + left_mode_indicator, show_cycle_hint, show_shortcuts_hint, show_queue_hint, @@ -3161,7 +3230,7 @@ impl ChatComposer { | FooterMode::ShortcutOverlay => None, } }; - let show_context = if matches!( + let show_right = if matches!( footer_props.mode, FooterMode::EscHint | FooterMode::QuitShortcutReminder @@ -3178,15 +3247,31 @@ impl ChatComposer { if let Some((summary_left, _)) = single_line_layout { match summary_left { SummaryLeft::Default => { - render_footer_from_props( - hint_rect, - buf, - footer_props, - self.collaboration_mode_indicator, - show_cycle_hint, - show_shortcuts_hint, - show_queue_hint, - ); + if status_line_active { + if let Some(line) = truncated_status_line.clone() { + render_footer_line(hint_rect, buf, line); + } else { + render_footer_from_props( + hint_rect, + buf, + &footer_props, + left_mode_indicator, + show_cycle_hint, + show_shortcuts_hint, + show_queue_hint, + ); + } + } else { + render_footer_from_props( + hint_rect, + buf, + &footer_props, + left_mode_indicator, + show_cycle_hint, + show_shortcuts_hint, + show_queue_hint, + ); + } } SummaryLeft::Custom(line) => { render_footer_line(hint_rect, buf, line); @@ -3199,11 +3284,15 @@ impl ChatComposer { } } else if let Some(items) = self.footer_hint_override.as_ref() { render_footer_hint_items(hint_rect, buf, items); + } else if status_line_active { + if let Some(line) = truncated_status_line { + render_footer_line(hint_rect, buf, line); + } } else { render_footer_from_props( hint_rect, buf, - footer_props, + &footer_props, self.collaboration_mode_indicator, show_cycle_hint, show_shortcuts_hint, @@ -3211,8 +3300,8 @@ impl ChatComposer { ); } - if show_context { - render_context_right(hint_rect, buf, &context_line); + if show_right && let Some(line) = &right_line { + render_context_right(hint_rect, buf, line); } } } @@ -3488,7 +3577,7 @@ mod tests { ); setup(&mut composer); let footer_props = composer.footer_props(); - let footer_lines = footer_height(footer_props); + let footer_lines = footer_height(&footer_props); let footer_spacing = ChatComposer::footer_spacing(footer_lines); let height = footer_lines + footer_spacing + 8; let mut terminal = Terminal::new(TestBackend::new(width, height)).unwrap(); diff --git a/codex-rs/tui/src/bottom_pane/footer.rs b/codex-rs/tui/src/bottom_pane/footer.rs index 4893f2f6a0..f6f61acf4b 100644 --- a/codex-rs/tui/src/bottom_pane/footer.rs +++ b/codex-rs/tui/src/bottom_pane/footer.rs @@ -53,7 +53,7 @@ use ratatui::widgets::Widget; /// (`render_footer_from_props` or the single-line collapse logic). The footer /// treats these values as authoritative and does not attempt to infer missing /// state (for example, it does not query whether a task is running). -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub(crate) struct FooterProps { pub(crate) mode: FooterMode, pub(crate) esc_backtrack_hint: bool, @@ -68,6 +68,8 @@ pub(crate) struct FooterProps { pub(crate) quit_shortcut_key: KeyBinding, pub(crate) context_window_percent: Option, pub(crate) context_window_used_tokens: Option, + pub(crate) status_line_value: Option>, + pub(crate) status_line_enabled: bool, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -168,7 +170,7 @@ pub(crate) fn reset_mode_after_activity(current: FooterMode) -> FooterMode { } } -pub(crate) fn footer_height(props: FooterProps) -> u16 { +pub(crate) fn footer_height(props: &FooterProps) -> u16 { let show_shortcuts_hint = match props.mode { FooterMode::ComposerEmpty => true, FooterMode::QuitShortcutReminder @@ -206,7 +208,7 @@ pub(crate) fn render_footer_line(area: Rect, buf: &mut Buffer, line: Line<'stati pub(crate) fn render_footer_from_props( area: Rect, buf: &mut Buffer, - props: FooterProps, + props: &FooterProps, collaboration_mode_indicator: Option, show_cycle_hint: bool, show_shortcuts_hint: bool, @@ -448,6 +450,13 @@ pub(crate) fn single_line_footer_layout( (SummaryLeft::None, true) } +pub(crate) fn mode_indicator_line( + indicator: Option, + show_cycle_hint: bool, +) -> Option> { + indicator.map(|indicator| Line::from(vec![indicator.styled_span(show_cycle_hint)])) +} + fn right_aligned_x(area: Rect, content_width: u16) -> Option { if area.is_empty() { return None; @@ -471,6 +480,20 @@ fn right_aligned_x(area: Rect, content_width: u16) -> Option { ) } +pub(crate) fn max_left_width_for_right(area: Rect, right_width: u16) -> Option { + let context_x = right_aligned_x(area, right_width)?; + let left_start = area.x + FOOTER_INDENT_COLS as u16; + + // minimal one column gap between left and right + let gap = FOOTER_CONTEXT_GAP_COLS; + + if context_x <= left_start + gap { + return Some(0); + } + + Some(context_x.saturating_sub(left_start + gap)) +} + pub(crate) fn can_show_left_with_context(area: Rect, left_width: u16, context_width: u16) -> bool { let Some(context_x) = right_aligned_x(area, context_width) else { return true; @@ -534,12 +557,22 @@ pub(crate) fn render_footer_hint_items(area: Rect, buf: &mut Buffer, items: &[(S /// fallback decisions live in `single_line_footer_layout`; this function only /// formats the chosen/default content. fn footer_from_props_lines( - props: FooterProps, + props: &FooterProps, collaboration_mode_indicator: Option, show_cycle_hint: bool, show_shortcuts_hint: bool, show_queue_hint: bool, ) -> Vec> { + // If status line content is present, show it for base modes. + if props.status_line_enabled + && let Some(status_line) = &props.status_line_value + && matches!( + props.mode, + FooterMode::ComposerEmpty | FooterMode::ComposerHasDraft + ) + { + return vec![status_line.clone().dim()]; + } match props.mode { FooterMode::QuitShortcutReminder => { vec![quit_shortcut_reminder_line(props.quit_shortcut_key)] @@ -580,7 +613,7 @@ fn footer_from_props_lines( } pub(crate) fn footer_line_width( - props: FooterProps, + props: &FooterProps, collaboration_mode_indicator: Option, show_cycle_hint: bool, show_shortcuts_hint: bool, @@ -957,31 +990,27 @@ const SHORTCUTS: &[ShortcutDescriptor] = &[ #[cfg(test)] mod tests { use super::*; + use crate::bottom_pane::selection_popup_common::truncate_line_with_ellipsis_if_overflow; + use crate::test_backend::VT100Backend; use insta::assert_snapshot; use pretty_assertions::assert_eq; use ratatui::Terminal; + use ratatui::backend::Backend; use ratatui::backend::TestBackend; fn snapshot_footer(name: &str, props: FooterProps) { - snapshot_footer_with_mode_indicator(name, 80, props, None); + snapshot_footer_with_mode_indicator(name, 80, &props, None); } - fn snapshot_footer_with_mode_indicator( - name: &str, - width: u16, - props: FooterProps, + fn draw_footer_frame( + terminal: &mut Terminal, + height: u16, + props: &FooterProps, collaboration_mode_indicator: Option, ) { - let height = footer_height(props).max(1); - let mut terminal = Terminal::new(TestBackend::new(width, height)).unwrap(); terminal .draw(|f| { let area = Rect::new(0, 0, f.area().width, height); - let context_line = context_window_line( - props.context_window_percent, - props.context_window_used_tokens, - ); - let context_width = context_line.width() as u16; let show_cycle_hint = !props.is_task_running; let show_shortcuts_hint = match props.mode { FooterMode::ComposerEmpty => true, @@ -997,53 +1026,118 @@ mod tests { | FooterMode::ShortcutOverlay | FooterMode::EscHint => false, }; - let left_width = footer_line_width( - props, - collaboration_mode_indicator, - show_cycle_hint, - show_shortcuts_hint, - show_queue_hint, - ); + let left_mode_indicator = if props.status_line_enabled { + None + } else { + collaboration_mode_indicator + }; + let available_width = area.width.saturating_sub(FOOTER_INDENT_COLS as u16) as usize; + let mut truncated_status_line = if props.status_line_enabled + && matches!( + props.mode, + FooterMode::ComposerEmpty | FooterMode::ComposerHasDraft + ) { + props + .status_line_value + .as_ref() + .map(|line| line.clone().dim()) + .map(|line| truncate_line_with_ellipsis_if_overflow(line, available_width)) + } else { + None + }; + let mut left_width = if props.status_line_enabled { + truncated_status_line + .as_ref() + .map(|line| line.width() as u16) + .unwrap_or(0) + } else { + footer_line_width( + props, + left_mode_indicator, + show_cycle_hint, + show_shortcuts_hint, + show_queue_hint, + ) + }; + let right_line = if props.status_line_enabled { + let full = mode_indicator_line(collaboration_mode_indicator, show_cycle_hint); + let compact = mode_indicator_line(collaboration_mode_indicator, false); + let full_width = full.as_ref().map(|line| line.width() as u16).unwrap_or(0); + if can_show_left_with_context(area, left_width, full_width) { + full + } else { + compact + } + } else { + Some(context_window_line( + props.context_window_percent, + props.context_window_used_tokens, + )) + }; + let right_width = right_line + .as_ref() + .map(|line| line.width() as u16) + .unwrap_or(0); + if props.status_line_enabled + && let Some(max_left) = max_left_width_for_right(area, right_width) + && left_width > max_left + && let Some(line) = props + .status_line_value + .as_ref() + .map(|line| line.clone().dim()) + .map(|line| { + truncate_line_with_ellipsis_if_overflow(line, max_left as usize) + }) + { + left_width = line.width() as u16; + truncated_status_line = Some(line); + } let can_show_left_and_context = - can_show_left_with_context(area, left_width, context_width); + can_show_left_with_context(area, left_width, right_width); if matches!( props.mode, FooterMode::ComposerEmpty | FooterMode::ComposerHasDraft ) { let (summary_left, show_context) = single_line_footer_layout( area, - context_width, - collaboration_mode_indicator, + right_width, + left_mode_indicator, show_cycle_hint, show_shortcuts_hint, show_queue_hint, ); match summary_left { SummaryLeft::Default => { - render_footer_from_props( - area, - f.buffer_mut(), - props, - collaboration_mode_indicator, - show_cycle_hint, - show_shortcuts_hint, - show_queue_hint, - ); + if props.status_line_enabled { + if let Some(line) = truncated_status_line.clone() { + render_footer_line(area, f.buffer_mut(), line); + } + } else { + render_footer_from_props( + area, + f.buffer_mut(), + props, + left_mode_indicator, + show_cycle_hint, + show_shortcuts_hint, + show_queue_hint, + ); + } } SummaryLeft::Custom(line) => { render_footer_line(area, f.buffer_mut(), line); } SummaryLeft::None => {} } - if show_context { - render_context_right(area, f.buffer_mut(), &context_line); + if show_context && let Some(line) = &right_line { + render_context_right(area, f.buffer_mut(), line); } } else { render_footer_from_props( area, f.buffer_mut(), props, - collaboration_mode_indicator, + left_mode_indicator, show_cycle_hint, show_shortcuts_hint, show_queue_hint, @@ -1055,15 +1149,37 @@ mod tests { | FooterMode::QuitShortcutReminder | FooterMode::ShortcutOverlay ); - if show_context { - render_context_right(area, f.buffer_mut(), &context_line); + if show_context && let Some(line) = &right_line { + render_context_right(area, f.buffer_mut(), line); } } }) .unwrap(); + } + + fn snapshot_footer_with_mode_indicator( + name: &str, + width: u16, + props: &FooterProps, + collaboration_mode_indicator: Option, + ) { + let height = footer_height(props).max(1); + let mut terminal = Terminal::new(TestBackend::new(width, height)).unwrap(); + draw_footer_frame(&mut terminal, height, props, collaboration_mode_indicator); assert_snapshot!(name, terminal.backend()); } + fn render_footer_with_mode_indicator( + width: u16, + props: &FooterProps, + collaboration_mode_indicator: Option, + ) -> String { + let height = footer_height(props).max(1); + let mut terminal = Terminal::new(VT100Backend::new(width, height)).expect("terminal"); + draw_footer_frame(&mut terminal, height, props, collaboration_mode_indicator); + terminal.backend().vt100().screen().contents() + } + #[test] fn footer_snapshots() { snapshot_footer( @@ -1079,6 +1195,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }, ); @@ -1095,6 +1213,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }, ); @@ -1111,6 +1231,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }, ); @@ -1127,6 +1249,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }, ); @@ -1143,6 +1267,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }, ); @@ -1159,6 +1285,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }, ); @@ -1175,6 +1303,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }, ); @@ -1191,6 +1321,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: Some(72), context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }, ); @@ -1207,6 +1339,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: Some(123_456), + status_line_value: None, + status_line_enabled: false, }, ); @@ -1223,6 +1357,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }, ); @@ -1239,6 +1375,8 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }, ); @@ -1253,19 +1391,21 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }; snapshot_footer_with_mode_indicator( "footer_mode_indicator_wide", 120, - props, + &props, Some(CollaborationModeIndicator::Plan), ); snapshot_footer_with_mode_indicator( "footer_mode_indicator_narrow_overlap_hides", 50, - props, + &props, Some(CollaborationModeIndicator::Plan), ); @@ -1280,14 +1420,161 @@ mod tests { quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), context_window_percent: None, context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, }; snapshot_footer_with_mode_indicator( "footer_mode_indicator_running_hides_hint", 120, - props, + &props, Some(CollaborationModeIndicator::Plan), ); + + let props = FooterProps { + mode: FooterMode::ComposerEmpty, + esc_backtrack_hint: false, + use_shift_enter_hint: false, + is_task_running: false, + steer_enabled: false, + collaboration_modes_enabled: false, + is_wsl: false, + quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), + context_window_percent: None, + context_window_used_tokens: None, + status_line_value: Some(Line::from("Status line content".to_string())), + status_line_enabled: true, + }; + + snapshot_footer("footer_status_line_overrides_shortcuts", props); + + let props = FooterProps { + mode: FooterMode::ComposerEmpty, + esc_backtrack_hint: false, + use_shift_enter_hint: false, + is_task_running: false, + steer_enabled: false, + collaboration_modes_enabled: true, + is_wsl: false, + quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), + context_window_percent: Some(50), + context_window_used_tokens: None, + status_line_value: None, // command timed out / empty + status_line_enabled: true, + }; + + snapshot_footer_with_mode_indicator( + "footer_status_line_enabled_mode_right", + 120, + &props, + Some(CollaborationModeIndicator::Plan), + ); + + let props = FooterProps { + mode: FooterMode::ComposerEmpty, + esc_backtrack_hint: false, + use_shift_enter_hint: false, + is_task_running: false, + steer_enabled: false, + collaboration_modes_enabled: true, + is_wsl: false, + quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), + context_window_percent: Some(50), + context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: false, + }; + + snapshot_footer_with_mode_indicator( + "footer_status_line_disabled_context_right", + 120, + &props, + Some(CollaborationModeIndicator::Plan), + ); + + let props = FooterProps { + mode: FooterMode::ComposerEmpty, + esc_backtrack_hint: false, + use_shift_enter_hint: false, + is_task_running: false, + steer_enabled: false, + collaboration_modes_enabled: false, + is_wsl: false, + quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), + context_window_percent: Some(50), + context_window_used_tokens: None, + status_line_value: None, + status_line_enabled: true, + }; + + // has status line and no collaboration mode + snapshot_footer_with_mode_indicator( + "footer_status_line_enabled_no_mode_right", + 120, + &props, + None, + ); + + let props = FooterProps { + mode: FooterMode::ComposerEmpty, + esc_backtrack_hint: false, + use_shift_enter_hint: false, + is_task_running: false, + steer_enabled: false, + collaboration_modes_enabled: true, + is_wsl: false, + quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), + context_window_percent: Some(50), + context_window_used_tokens: None, + status_line_value: Some(Line::from( + "Status line content that should truncate before the mode indicator".to_string(), + )), + status_line_enabled: true, + }; + + snapshot_footer_with_mode_indicator( + "footer_status_line_truncated_with_gap", + 40, + &props, + Some(CollaborationModeIndicator::Plan), + ); + } + + #[test] + fn footer_status_line_truncates_to_keep_mode_indicator() { + let props = FooterProps { + mode: FooterMode::ComposerEmpty, + esc_backtrack_hint: false, + use_shift_enter_hint: false, + is_task_running: false, + steer_enabled: false, + collaboration_modes_enabled: true, + is_wsl: false, + quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')), + context_window_percent: Some(50), + context_window_used_tokens: None, + status_line_value: Some(Line::from( + "Status line content that is definitely too long to fit alongside the mode label" + .to_string(), + )), + status_line_enabled: true, + }; + + let screen = + render_footer_with_mode_indicator(80, &props, Some(CollaborationModeIndicator::Plan)); + let collapsed = screen.split_whitespace().collect::>().join(" "); + assert!( + collapsed.contains("Plan mode"), + "mode indicator should remain visible" + ); + assert!( + !collapsed.contains("shift+tab to cycle"), + "compact mode indicator should be used when space is tight" + ); + assert!( + screen.contains('…'), + "status line should be truncated with ellipsis to keep mode indicator" + ); } #[test] diff --git a/codex-rs/tui/src/bottom_pane/list_selection_view.rs b/codex-rs/tui/src/bottom_pane/list_selection_view.rs index 095a095500..9bc046f010 100644 --- a/codex-rs/tui/src/bottom_pane/list_selection_view.rs +++ b/codex-rs/tui/src/bottom_pane/list_selection_view.rs @@ -23,14 +23,25 @@ use super::CancellationEvent; use super::bottom_pane_view::BottomPaneView; use super::popup_consts::MAX_POPUP_ROWS; use super::scroll_state::ScrollState; +pub(crate) use super::selection_popup_common::ColumnWidthMode; use super::selection_popup_common::GenericDisplayRow; use super::selection_popup_common::measure_rows_height; +use super::selection_popup_common::measure_rows_height_stable_col_widths; +use super::selection_popup_common::measure_rows_height_with_col_width_mode; use super::selection_popup_common::render_rows; +use super::selection_popup_common::render_rows_stable_col_widths; +use super::selection_popup_common::render_rows_with_col_width_mode; use unicode_width::UnicodeWidthStr; /// One selectable item in the generic selection list. pub(crate) type SelectionAction = Box; +/// One row in a [`ListSelectionView`] selection list. +/// +/// This is the source-of-truth model for row state before filtering and +/// formatting into render rows. A row is treated as disabled when either +/// `is_disabled` is true or `disabled_reason` is present; disabled rows cannot +/// be accepted and are skipped by keyboard navigation. #[derive(Default)] pub(crate) struct SelectionItem { pub name: String, @@ -46,6 +57,16 @@ pub(crate) struct SelectionItem { pub disabled_reason: Option, } +/// Construction-time configuration for [`ListSelectionView`]. +/// +/// This config is consumed once by [`ListSelectionView::new`]. After +/// construction, mutable interaction state (filtering, scrolling, and selected +/// row) lives on the view itself. +/// +/// `col_width_mode` controls column width mode in selection lists: +/// `AutoVisible` (default) measures only rows visible in the viewport +/// `AutoAllRows` measures all rows to ensure stable column widths as the user scrolls +/// `Fixed` used a fixed 30/70 split between columns pub(crate) struct SelectionViewParams { pub title: Option, pub subtitle: Option, @@ -54,6 +75,7 @@ pub(crate) struct SelectionViewParams { pub items: Vec, pub is_searchable: bool, pub search_placeholder: Option, + pub col_width_mode: ColumnWidthMode, pub header: Box, pub initial_selected_idx: Option, } @@ -68,12 +90,18 @@ impl Default for SelectionViewParams { items: Vec::new(), is_searchable: false, search_placeholder: None, + col_width_mode: ColumnWidthMode::AutoVisible, header: Box::new(()), initial_selected_idx: None, } } } +/// Runtime state for rendering and interacting with a list-based selection popup. +/// +/// This type is the single authority for filtered index mapping between +/// visible rows and source items and for preserving selection while filters +/// change. pub(crate) struct ListSelectionView { footer_note: Option>, footer_hint: Option>, @@ -84,6 +112,7 @@ pub(crate) struct ListSelectionView { is_searchable: bool, search_query: String, search_placeholder: Option, + col_width_mode: ColumnWidthMode, filtered_indices: Vec, last_selected_actual_idx: Option, header: Box, @@ -91,6 +120,13 @@ pub(crate) struct ListSelectionView { } impl ListSelectionView { + /// Create a selection popup view with filtering, scrolling, and callbacks wired. + /// + /// The constructor normalizes header/title composition and immediately + /// applies filtering so `ScrollState` starts in a valid visible range. + /// When search is enabled, rows without `search_value` will disappear as + /// soon as the query is non-empty, which can look like dropped data unless + /// callers intentionally populate that field. pub fn new(params: SelectionViewParams, app_event_tx: AppEventSender) -> Self { let mut header = params.header; if params.title.is_some() || params.subtitle.is_some() { @@ -116,6 +152,7 @@ impl ListSelectionView { } else { None }, + col_width_mode: params.col_width_mode, filtered_indices: Vec::new(), last_selected_actual_idx: None, header, @@ -434,12 +471,27 @@ impl Renderable for ListSelectionView { // Build the same display rows used by the renderer so wrapping math matches. let rows = self.build_rows(); let rows_width = Self::rows_width(width); - let rows_height = measure_rows_height( - &rows, - &self.state, - MAX_POPUP_ROWS, - rows_width.saturating_add(1), - ); + let rows_height = match self.col_width_mode { + ColumnWidthMode::AutoVisible => measure_rows_height( + &rows, + &self.state, + MAX_POPUP_ROWS, + rows_width.saturating_add(1), + ), + ColumnWidthMode::AutoAllRows => measure_rows_height_stable_col_widths( + &rows, + &self.state, + MAX_POPUP_ROWS, + rows_width.saturating_add(1), + ), + ColumnWidthMode::Fixed => measure_rows_height_with_col_width_mode( + &rows, + &self.state, + MAX_POPUP_ROWS, + rows_width.saturating_add(1), + ColumnWidthMode::Fixed, + ), + }; // Subtract 4 for the padding on the left and right of the header. let mut height = self.header.desired_height(width.saturating_sub(4)); @@ -483,12 +535,27 @@ impl Renderable for ListSelectionView { .desired_height(outer_content_area.width.saturating_sub(4)); let rows = self.build_rows(); let rows_width = Self::rows_width(outer_content_area.width); - let rows_height = measure_rows_height( - &rows, - &self.state, - MAX_POPUP_ROWS, - rows_width.saturating_add(1), - ); + let rows_height = match self.col_width_mode { + ColumnWidthMode::AutoVisible => measure_rows_height( + &rows, + &self.state, + MAX_POPUP_ROWS, + rows_width.saturating_add(1), + ), + ColumnWidthMode::AutoAllRows => measure_rows_height_stable_col_widths( + &rows, + &self.state, + MAX_POPUP_ROWS, + rows_width.saturating_add(1), + ), + ColumnWidthMode::Fixed => measure_rows_height_with_col_width_mode( + &rows, + &self.state, + MAX_POPUP_ROWS, + rows_width.saturating_add(1), + ColumnWidthMode::Fixed, + ), + }; let [header_area, _, search_area, list_area] = Layout::vertical([ Constraint::Max(header_height), Constraint::Max(1), @@ -529,14 +596,33 @@ impl Renderable for ListSelectionView { width: rows_width.max(1), height: list_area.height, }; - render_rows( - render_area, - buf, - &rows, - &self.state, - render_area.height as usize, - "no matches", - ); + match self.col_width_mode { + ColumnWidthMode::AutoVisible => render_rows( + render_area, + buf, + &rows, + &self.state, + render_area.height as usize, + "no matches", + ), + ColumnWidthMode::AutoAllRows => render_rows_stable_col_widths( + render_area, + buf, + &rows, + &self.state, + render_area.height as usize, + "no matches", + ), + ColumnWidthMode::Fixed => render_rows_with_col_width_mode( + render_area, + buf, + &rows, + &self.state, + render_area.height as usize, + "no matches", + ColumnWidthMode::Fixed, + ), + } } if footer_area.height > 0 { @@ -585,7 +671,9 @@ mod tests { use super::*; use crate::app_event::AppEvent; use crate::bottom_pane::popup_consts::standard_popup_hint_line; + use crossterm::event::KeyCode; use insta::assert_snapshot; + use pretty_assertions::assert_eq; use ratatui::layout::Rect; use tokio::sync::mpsc::unbounded_channel; @@ -647,6 +735,55 @@ mod tests { lines.join("\n") } + fn description_col(rendered: &str, item_marker: &str, description: &str) -> usize { + let line = rendered + .lines() + .find(|line| line.contains(item_marker) && line.contains(description)) + .expect("expected rendered line to contain row marker and description"); + line.find(description) + .expect("expected rendered line to contain description") + } + + fn make_scrolling_width_items() -> Vec { + let mut items: Vec = (1..=8) + .map(|idx| SelectionItem { + name: format!("Item {idx}"), + description: Some(format!("desc {idx}")), + dismiss_on_select: true, + ..Default::default() + }) + .collect(); + items.push(SelectionItem { + name: "Item 9 with an intentionally much longer name".to_string(), + description: Some("desc 9".to_string()), + dismiss_on_select: true, + ..Default::default() + }); + items + } + + fn render_before_after_scroll_snapshot(col_width_mode: ColumnWidthMode, width: u16) -> String { + let (tx_raw, _rx) = unbounded_channel::(); + let tx = AppEventSender::new(tx_raw); + let mut view = ListSelectionView::new( + SelectionViewParams { + title: Some("Debug".to_string()), + items: make_scrolling_width_items(), + col_width_mode, + ..Default::default() + }, + tx, + ); + + let before_scroll = render_lines_with_width(&view, width); + for _ in 0..8 { + view.handle_key_event(KeyEvent::from(KeyCode::Down)); + } + let after_scroll = render_lines_with_width(&view, width); + + format!("before scroll:\n{before_scroll}\n\nafter scroll:\n{after_scroll}") + } + #[test] fn renders_blank_line_between_title_and_items_without_subtitle() { let view = make_selection_view(None); @@ -921,4 +1058,96 @@ mod tests { render_lines_with_width(&view, 24) ); } + + #[test] + fn snapshot_auto_visible_col_width_mode_scroll_behavior() { + assert_snapshot!( + "list_selection_col_width_mode_auto_visible_scroll", + render_before_after_scroll_snapshot(ColumnWidthMode::AutoVisible, 96) + ); + } + + #[test] + fn snapshot_auto_all_rows_col_width_mode_scroll_behavior() { + assert_snapshot!( + "list_selection_col_width_mode_auto_all_rows_scroll", + render_before_after_scroll_snapshot(ColumnWidthMode::AutoAllRows, 96) + ); + } + + #[test] + fn snapshot_fixed_col_width_mode_scroll_behavior() { + assert_snapshot!( + "list_selection_col_width_mode_fixed_scroll", + render_before_after_scroll_snapshot(ColumnWidthMode::Fixed, 96) + ); + } + + #[test] + fn auto_all_rows_col_width_does_not_shift_when_scrolling() { + let (tx_raw, _rx) = unbounded_channel::(); + let tx = AppEventSender::new(tx_raw); + + let mut view = ListSelectionView::new( + SelectionViewParams { + title: Some("Debug".to_string()), + items: make_scrolling_width_items(), + col_width_mode: ColumnWidthMode::AutoAllRows, + ..Default::default() + }, + tx, + ); + + let before_scroll = render_lines_with_width(&view, 96); + for _ in 0..8 { + view.handle_key_event(KeyEvent::from(KeyCode::Down)); + } + let after_scroll = render_lines_with_width(&view, 96); + + assert!( + after_scroll.contains("9. Item 9 with an intentionally much longer name"), + "expected the scrolled view to include the longer row:\n{after_scroll}" + ); + + let before_col = description_col(&before_scroll, "8. Item 8", "desc 8"); + let after_col = description_col(&after_scroll, "8. Item 8", "desc 8"); + assert_eq!( + before_col, after_col, + "description column changed across scroll:\nbefore:\n{before_scroll}\nafter:\n{after_scroll}" + ); + } + + #[test] + fn fixed_col_width_is_30_70_and_does_not_shift_when_scrolling() { + let (tx_raw, _rx) = unbounded_channel::(); + let tx = AppEventSender::new(tx_raw); + let width = 96; + let mut view = ListSelectionView::new( + SelectionViewParams { + title: Some("Debug".to_string()), + items: make_scrolling_width_items(), + col_width_mode: ColumnWidthMode::Fixed, + ..Default::default() + }, + tx, + ); + + let before_scroll = render_lines_with_width(&view, width); + let before_col = description_col(&before_scroll, "8. Item 8", "desc 8"); + let expected_desc_col = ((width.saturating_sub(2) as usize) * 3) / 10; + assert_eq!( + before_col, expected_desc_col, + "fixed mode should place description column at a 30/70 split:\n{before_scroll}" + ); + + for _ in 0..8 { + view.handle_key_event(KeyEvent::from(KeyCode::Down)); + } + let after_scroll = render_lines_with_width(&view, width); + let after_col = description_col(&after_scroll, "8. Item 8", "desc 8"); + assert_eq!( + before_col, after_col, + "fixed description column changed across scroll:\nbefore:\n{before_scroll}\nafter:\n{after_scroll}" + ); + } } diff --git a/codex-rs/tui/src/bottom_pane/mod.rs b/codex-rs/tui/src/bottom_pane/mod.rs index cd5e2adb80..b9c77fc72d 100644 --- a/codex-rs/tui/src/bottom_pane/mod.rs +++ b/codex-rs/tui/src/bottom_pane/mod.rs @@ -36,11 +36,14 @@ use crossterm::event::KeyCode; use crossterm::event::KeyEvent; use ratatui::buffer::Buffer; use ratatui::layout::Rect; +use ratatui::text::Line; use std::time::Duration; mod app_link_view; mod approval_overlay; +mod multi_select_picker; mod request_user_input; +mod status_line_setup; pub(crate) use app_link_view::AppLinkView; pub(crate) use approval_overlay::ApprovalOverlay; pub(crate) use approval_overlay::ApprovalRequest; @@ -65,6 +68,7 @@ mod skill_popup; mod skills_toggle_view; mod slash_commands; pub(crate) use footer::CollaborationModeIndicator; +pub(crate) use list_selection_view::ColumnWidthMode; pub(crate) use list_selection_view::SelectionViewParams; mod feedback_view; pub(crate) use feedback_view::FeedbackAudience; @@ -73,6 +77,8 @@ pub(crate) use feedback_view::feedback_selection_params; pub(crate) use feedback_view::feedback_upload_consent_params; pub(crate) use skills_toggle_view::SkillsToggleItem; pub(crate) use skills_toggle_view::SkillsToggleView; +pub(crate) use status_line_setup::StatusLineItem; +pub(crate) use status_line_setup::StatusLineSetupView; mod paste_burst; pub mod popup_consts; mod queued_user_messages; @@ -861,6 +867,16 @@ impl BottomPane { RenderableItem::Owned(Box::new(flex2)) } } + + pub(crate) fn set_status_line(&mut self, status_line: Option>) { + self.composer.set_status_line(status_line); + self.request_redraw(); + } + + pub(crate) fn set_status_line_enabled(&mut self, enabled: bool) { + self.composer.set_status_line_enabled(enabled); + self.request_redraw(); + } } impl Renderable for BottomPane { diff --git a/codex-rs/tui/src/bottom_pane/multi_select_picker.rs b/codex-rs/tui/src/bottom_pane/multi_select_picker.rs new file mode 100644 index 0000000000..0fb027a57a --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/multi_select_picker.rs @@ -0,0 +1,795 @@ +//! Multi-select picker widget for selecting multiple items from a list. +//! +//! This module provides a fuzzy-searchable, scrollable picker that allows users +//! to toggle multiple items on/off. It supports: +//! +//! - **Fuzzy search**: Type to filter items by name +//! - **Toggle selection**: Space to toggle items on/off +//! - **Reordering**: Optional left/right arrow support to reorder items +//! - **Live preview**: Optional callback to show a preview of current selections +//! - **Callbacks**: Hooks for change, confirm, and cancel events +//! +//! # Example +//! +//! ```ignore +//! let picker = MultiSelectPicker::new( +//! "Select Items".to_string(), +//! Some("Choose which items to enable".to_string()), +//! app_event_tx, +//! ) +//! .items(vec![ +//! MultiSelectItem { id: "a".into(), name: "Item A".into(), description: None, enabled: true }, +//! MultiSelectItem { id: "b".into(), name: "Item B".into(), description: None, enabled: false }, +//! ]) +//! .on_confirm(|selected_ids, tx| { /* handle confirmation */ }) +//! .build(); +//! ``` + +use codex_common::fuzzy_match::fuzzy_match; +use crossterm::event::KeyCode; +use crossterm::event::KeyEvent; +use crossterm::event::KeyModifiers; +use ratatui::buffer::Buffer; +use ratatui::layout::Constraint; +use ratatui::layout::Layout; +use ratatui::layout::Rect; +use ratatui::style::Stylize; +use ratatui::text::Line; +use ratatui::text::Span; +use ratatui::widgets::Block; +use ratatui::widgets::Widget; + +use super::selection_popup_common::GenericDisplayRow; +use crate::app_event_sender::AppEventSender; +use crate::bottom_pane::CancellationEvent; +use crate::bottom_pane::bottom_pane_view::BottomPaneView; +use crate::bottom_pane::popup_consts::MAX_POPUP_ROWS; +use crate::bottom_pane::scroll_state::ScrollState; +use crate::bottom_pane::selection_popup_common::render_rows_single_line; +use crate::bottom_pane::selection_popup_common::truncate_line_with_ellipsis_if_overflow; +use crate::key_hint; +use crate::render::Insets; +use crate::render::RectExt; +use crate::render::renderable::ColumnRenderable; +use crate::render::renderable::Renderable; +use crate::style::user_message_style; +use crate::text_formatting::truncate_text; + +/// Maximum display length for item names before truncation. +const ITEM_NAME_TRUNCATE_LEN: usize = 21; + +/// Placeholder text shown in the search input when empty. +const SEARCH_PLACEHOLDER: &str = "Type to search"; + +/// Prefix displayed before the search query (mimics a command prompt). +const SEARCH_PROMPT_PREFIX: &str = "> "; + +/// Direction for reordering items in the list. +enum Direction { + Up, + Down, +} + +/// Callback invoked when any item's state changes (toggled or reordered). +/// Receives the full list of items and the event sender. +pub type ChangeCallBack = Box; + +/// Callback invoked when the user confirms their selection (presses Enter). +/// Receives a list of IDs for all enabled items. +pub type ConfirmCallback = Box; + +/// Callback invoked when the user cancels the picker (presses Escape). +pub type CancelCallback = Box; + +/// Callback to generate an optional preview line based on current item states. +/// Returns `None` to hide the preview area. +pub type PreviewCallback = Box Option> + Send + Sync>; + +/// A single selectable item in the multi-select picker. +/// +/// Each item has a unique identifier, display name, optional description, +/// and an enabled/disabled state that can be toggled by the user. +#[derive(Default)] +pub(crate) struct MultiSelectItem { + /// Unique identifier returned in the confirm callback when this item is enabled. + pub id: String, + + /// Display name shown in the picker list. Will be truncated if too long. + pub name: String, + + /// Optional description shown alongside the name (dimmed). + pub description: Option, + + /// Whether this item is currently selected/enabled. + pub enabled: bool, +} + +/// A multi-select picker widget with fuzzy search and optional reordering. +/// +/// The picker displays a scrollable list of items with checkboxes. Users can: +/// - Type to fuzzy-search and filter the list +/// - Use Up/Down (or Ctrl+P/Ctrl+N) to navigate +/// - Press Space to toggle the selected item +/// - Press Enter to confirm and close +/// - Press Escape to cancel and close +/// - Use Left/Right arrows to reorder items (if ordering is enabled) +/// +/// Create instances using the builder pattern via [`MultiSelectPicker::new`]. +pub(crate) struct MultiSelectPicker { + /// All items in the picker (unfiltered). + items: Vec, + + /// Scroll and selection state for the visible list. + state: ScrollState, + + /// Whether the picker has been closed (confirmed or cancelled). + pub(crate) complete: bool, + + /// Channel for sending application events. + app_event_tx: AppEventSender, + + /// Header widget displaying title and subtitle. + header: Box, + + /// Footer line showing keyboard hints. + footer_hint: Line<'static>, + + /// Current search/filter query entered by the user. + search_query: String, + + /// Indices into `items` that match the current filter, in display order. + filtered_indices: Vec, + + /// Whether left/right arrow reordering is enabled. + ordering_enabled: bool, + + /// Optional callback to generate a preview line from current item states. + preview_builder: Option, + + /// Cached preview line (updated on item changes). + preview_line: Option>, + + /// Callback invoked when items change (toggle or reorder). + on_change: Option, + + /// Callback invoked when the user confirms their selection. + on_confirm: Option, + + /// Callback invoked when the user cancels the picker. + on_cancel: Option, +} + +impl MultiSelectPicker { + /// Creates a new builder for constructing a `MultiSelectPicker`. + /// + /// # Arguments + /// + /// * `title` - The main title displayed at the top of the picker + /// * `subtitle` - Optional subtitle displayed below the title (dimmed) + /// * `app_event_tx` - Event sender for dispatching application events + pub fn builder( + title: String, + subtitle: Option, + app_event_tx: AppEventSender, + ) -> MultiSelectPickerBuilder { + MultiSelectPickerBuilder::new(title, subtitle, app_event_tx) + } + + /// Applies the current search query to filter and sort items. + /// + /// Updates `filtered_indices` to contain only matching items, sorted by + /// fuzzy match score. Attempts to preserve the current selection if it + /// still matches the filter. + fn apply_filter(&mut self) { + // Filter + sort while preserving the current selection when possible. + let previously_selected = self + .state + .selected_idx + .and_then(|visible_idx| self.filtered_indices.get(visible_idx).copied()); + + let filter = self.search_query.trim(); + if filter.is_empty() { + self.filtered_indices = (0..self.items.len()).collect(); + } else { + let mut matches: Vec<(usize, i32)> = Vec::new(); + for (idx, item) in self.items.iter().enumerate() { + let display_name = item.name.as_str(); + if let Some((_indices, score)) = match_item(filter, display_name, &item.name) { + matches.push((idx, score)); + } + } + + matches.sort_by(|a, b| { + a.1.cmp(&b.1).then_with(|| { + let an = self.items[a.0].name.as_str(); + let bn = self.items[b.0].name.as_str(); + an.cmp(bn) + }) + }); + + self.filtered_indices = matches.into_iter().map(|(idx, _score)| idx).collect(); + } + + let len = self.filtered_indices.len(); + self.state.selected_idx = previously_selected + .and_then(|actual_idx| { + self.filtered_indices + .iter() + .position(|idx| *idx == actual_idx) + }) + .or_else(|| (len > 0).then_some(0)); + + let visible = Self::max_visible_rows(len); + self.state.clamp_selection(len); + self.state.ensure_visible(len, visible); + } + + /// Returns the number of items visible after filtering. + fn visible_len(&self) -> usize { + self.filtered_indices.len() + } + + /// Returns the maximum number of rows that can be displayed at once. + fn max_visible_rows(len: usize) -> usize { + MAX_POPUP_ROWS.min(len.max(1)) + } + + /// Calculates the width available for row content (accounts for borders). + fn rows_width(total_width: u16) -> u16 { + total_width.saturating_sub(2) + } + + /// Calculates the height needed for the row list area. + fn rows_height(&self, rows: &[GenericDisplayRow]) -> u16 { + rows.len().clamp(1, MAX_POPUP_ROWS).try_into().unwrap_or(1) + } + + /// Builds the display rows for all currently visible (filtered) items. + /// + /// Each row shows: `› [x] Item Name` where `›` indicates cursor position + /// and `[x]` or `[ ]` indicates enabled/disabled state. + fn build_rows(&self) -> Vec { + self.filtered_indices + .iter() + .enumerate() + .filter_map(|(visible_idx, actual_idx)| { + self.items.get(*actual_idx).map(|item| { + let is_selected = self.state.selected_idx == Some(visible_idx); + let prefix = if is_selected { '›' } else { ' ' }; + let marker = if item.enabled { 'x' } else { ' ' }; + let item_name = truncate_text(&item.name, ITEM_NAME_TRUNCATE_LEN); + let name = format!("{prefix} [{marker}] {item_name}"); + GenericDisplayRow { + name, + description: item.description.clone(), + ..Default::default() + } + }) + }) + .collect() + } + + /// Moves the selection cursor up, wrapping to the bottom if at the top. + fn move_up(&mut self) { + let len = self.visible_len(); + self.state.move_up_wrap(len); + let visible = Self::max_visible_rows(len); + self.state.ensure_visible(len, visible); + } + + /// Moves the selection cursor down, wrapping to the top if at the bottom. + fn move_down(&mut self) { + let len = self.visible_len(); + self.state.move_down_wrap(len); + let visible = Self::max_visible_rows(len); + self.state.ensure_visible(len, visible); + } + + /// Toggles the enabled state of the currently selected item. + /// + /// Updates the preview line and invokes the `on_change` callback if set. + fn toggle_selected(&mut self) { + let Some(idx) = self.state.selected_idx else { + return; + }; + let Some(actual_idx) = self.filtered_indices.get(idx).copied() else { + return; + }; + let Some(item) = self.items.get_mut(actual_idx) else { + return; + }; + + item.enabled = !item.enabled; + self.update_preview_line(); + if let Some(on_change) = &self.on_change { + on_change(&self.items, &self.app_event_tx); + } + } + + /// Confirms the current selection and closes the picker. + /// + /// Collects the IDs of all enabled items and passes them to the + /// `on_confirm` callback. Does nothing if already complete. + fn confirm_selection(&mut self) { + if self.complete { + return; + } + self.complete = true; + + if let Some(on_confirm) = &self.on_confirm { + let selected_ids: Vec = self + .items + .iter() + .filter(|item| item.enabled) + .map(|item| item.id.clone()) + .collect(); + on_confirm(&selected_ids, &self.app_event_tx); + } + } + + /// Moves the currently selected item up or down in the list. + /// + /// Only works when: + /// - The search query is empty (reordering is disabled during filtering) + /// - Ordering is enabled via [`MultiSelectPickerBuilder::enable_ordering`] + /// + /// Updates the preview line and invokes the `on_change` callback. + fn move_selected_item(&mut self, direction: Direction) { + if !self.search_query.is_empty() { + return; + } + + let Some(visible_idx) = self.state.selected_idx else { + return; + }; + let Some(actual_idx) = self.filtered_indices.get(visible_idx).copied() else { + return; + }; + + let len = self.items.len(); + if len == 0 { + return; + } + + let new_idx = match direction { + Direction::Up if actual_idx > 0 => actual_idx - 1, + Direction::Down if actual_idx + 1 < len => actual_idx + 1, + _ => return, + }; + + // move item in underlying list + self.items.swap(actual_idx, new_idx); + + self.update_preview_line(); + if let Some(on_change) = &self.on_change { + on_change(&self.items, &self.app_event_tx); + } + + // rebuild filtered indices to keep search/filter consistent + self.apply_filter(); + + // restore selection to moved item + let moved_idx = new_idx; + if let Some(new_visible_idx) = self + .filtered_indices + .iter() + .position(|idx| *idx == moved_idx) + { + self.state.selected_idx = Some(new_visible_idx); + } + } + + /// Regenerates the preview line using the preview callback. + /// + /// Called after any item state change (toggle or reorder). + fn update_preview_line(&mut self) { + self.preview_line = self + .preview_builder + .as_ref() + .and_then(|builder| builder(&self.items)); + } + + /// Closes the picker without confirming, invoking the `on_cancel` callback. + /// + /// Does nothing if already complete. + pub fn close(&mut self) { + if self.complete { + return; + } + self.complete = true; + if let Some(on_cancel) = &self.on_cancel { + on_cancel(&self.app_event_tx); + } + } +} + +impl BottomPaneView for MultiSelectPicker { + fn is_complete(&self) -> bool { + self.complete + } + + fn on_ctrl_c(&mut self) -> CancellationEvent { + self.close(); + CancellationEvent::Handled + } + + fn handle_key_event(&mut self, key_event: KeyEvent) { + match key_event { + KeyEvent { code: KeyCode::Left, .. } if self.ordering_enabled => { + self.move_selected_item(Direction::Up); + } + KeyEvent { code: KeyCode::Right, .. } if self.ordering_enabled => { + self.move_selected_item(Direction::Down); + } + KeyEvent { + code: KeyCode::Up, .. + } + | KeyEvent { + code: KeyCode::Char('p'), + modifiers: KeyModifiers::CONTROL, + .. + } + | KeyEvent { + code: KeyCode::Char('k'), + modifiers: KeyModifiers::CONTROL, + .. + } + | KeyEvent { + code: KeyCode::Char('\u{0010}'), + modifiers: KeyModifiers::NONE, + .. + } /* ^P */ => self.move_up(), + KeyEvent { + code: KeyCode::Down, + .. + } + | KeyEvent { + code: KeyCode::Char('j'), + modifiers: KeyModifiers::CONTROL, + .. + } + | KeyEvent { + code: KeyCode::Char('n'), + modifiers: KeyModifiers::CONTROL, + .. + } + | KeyEvent { + code: KeyCode::Char('\u{000e}'), + modifiers: KeyModifiers::NONE, + .. + } /* ^N */ => self.move_down(), + KeyEvent { + code: KeyCode::Backspace, + .. + } => { + self.search_query.pop(); + self.apply_filter(); + } + KeyEvent { + code: KeyCode::Char(' '), + modifiers: KeyModifiers::NONE, + .. + } => self.toggle_selected(), + KeyEvent { + code: KeyCode::Enter, + .. + } => self.confirm_selection(), + KeyEvent { + code: KeyCode::Esc, .. + } => { + self.close(); + } + KeyEvent { + code: KeyCode::Char(c), + modifiers, + .. + } if !modifiers.contains(KeyModifiers::CONTROL) + && !modifiers.contains(KeyModifiers::ALT) => + { + self.search_query.push(c); + self.apply_filter(); + } + _ => {} + } + } +} + +impl Renderable for MultiSelectPicker { + fn desired_height(&self, width: u16) -> u16 { + let rows = self.build_rows(); + let rows_height = self.rows_height(&rows); + let preview_height = if self.preview_line.is_some() { 1 } else { 0 }; + + let mut height = self.header.desired_height(width.saturating_sub(4)); + height = height.saturating_add(rows_height + 3); + height = height.saturating_add(2); + height.saturating_add(1 + preview_height) + } + + fn render(&self, area: Rect, buf: &mut Buffer) { + if area.height == 0 || area.width == 0 { + return; + } + + // Reserve the footer line for the key-hint row. + let preview_height = if self.preview_line.is_some() { 1 } else { 0 }; + let footer_height = 1 + preview_height; + let [content_area, footer_area] = + Layout::vertical([Constraint::Fill(1), Constraint::Length(footer_height)]).areas(area); + + Block::default() + .style(user_message_style()) + .render(content_area, buf); + + let header_height = self + .header + .desired_height(content_area.width.saturating_sub(4)); + let rows = self.build_rows(); + let rows_width = Self::rows_width(content_area.width); + let rows_height = self.rows_height(&rows); + let [header_area, _, search_area, list_area] = Layout::vertical([ + Constraint::Max(header_height), + Constraint::Max(1), + Constraint::Length(2), + Constraint::Length(rows_height), + ]) + .areas(content_area.inset(Insets::vh(1, 2))); + + self.header.render(header_area, buf); + + // Render the search prompt as two lines to mimic the composer. + if search_area.height >= 2 { + let [placeholder_area, input_area] = + Layout::vertical([Constraint::Length(1), Constraint::Length(1)]).areas(search_area); + Line::from(SEARCH_PLACEHOLDER.dim()).render(placeholder_area, buf); + let line = if self.search_query.is_empty() { + Line::from(vec![SEARCH_PROMPT_PREFIX.dim()]) + } else { + Line::from(vec![ + SEARCH_PROMPT_PREFIX.dim(), + self.search_query.clone().into(), + ]) + }; + line.render(input_area, buf); + } else if search_area.height > 0 { + let query_span = if self.search_query.is_empty() { + SEARCH_PLACEHOLDER.dim() + } else { + self.search_query.clone().into() + }; + Line::from(query_span).render(search_area, buf); + } + + if list_area.height > 0 { + let render_area = Rect { + x: list_area.x.saturating_sub(2), + y: list_area.y, + width: rows_width.max(1), + height: list_area.height, + }; + render_rows_single_line( + render_area, + buf, + &rows, + &self.state, + render_area.height as usize, + "no matches", + ); + } + + let hint_area = if let Some(preview_line) = &self.preview_line { + let [preview_area, hint_area] = + Layout::vertical([Constraint::Length(1), Constraint::Length(1)]).areas(footer_area); + let preview_area = Rect { + x: preview_area.x + 2, + y: preview_area.y, + width: preview_area.width.saturating_sub(2), + height: preview_area.height, + }; + let max_preview_width = preview_area.width.saturating_sub(2) as usize; + let preview_line = + truncate_line_with_ellipsis_if_overflow(preview_line.clone(), max_preview_width); + preview_line.render(preview_area, buf); + hint_area + } else { + footer_area + }; + let hint_area = Rect { + x: hint_area.x + 2, + y: hint_area.y, + width: hint_area.width.saturating_sub(2), + height: hint_area.height, + }; + self.footer_hint.clone().dim().render(hint_area, buf); + } +} + +/// Builder for constructing a [`MultiSelectPicker`] with a fluent API. +/// +/// # Example +/// +/// ```ignore +/// let picker = MultiSelectPicker::new("Title".into(), None, tx) +/// .items(items) +/// .enable_ordering() +/// .on_preview(|items| Some(Line::from("Preview"))) +/// .on_confirm(|ids, tx| { /* handle */ }) +/// .on_cancel(|tx| { /* handle */ }) +/// .build(); +/// ``` +pub(crate) struct MultiSelectPickerBuilder { + title: String, + subtitle: Option, + instructions: Vec>, + items: Vec, + ordering_enabled: bool, + app_event_tx: AppEventSender, + preview_builder: Option, + on_change: Option, + on_confirm: Option, + on_cancel: Option, +} + +impl MultiSelectPickerBuilder { + /// Creates a new builder with the given title, optional subtitle, and event sender. + pub fn new(title: String, subtitle: Option, app_event_tx: AppEventSender) -> Self { + Self { + title, + subtitle, + instructions: Vec::new(), + items: Vec::new(), + ordering_enabled: false, + app_event_tx, + preview_builder: None, + on_change: None, + on_confirm: None, + on_cancel: None, + } + } + + /// Sets the list of selectable items. + pub fn items(mut self, items: Vec) -> Self { + self.items = items; + self + } + + /// Sets custom instruction spans for the footer hint line. + /// + /// If not set, default instructions are shown (Space to toggle, Enter to + /// confirm, Escape to close). + pub fn instructions(mut self, instructions: Vec>) -> Self { + self.instructions = instructions; + self + } + + /// Enables left/right arrow keys for reordering items. + /// + /// Reordering is only active when the search query is empty. + pub fn enable_ordering(mut self) -> Self { + self.ordering_enabled = true; + self + } + + /// Sets a callback to generate a preview line from the current item states. + /// + /// The callback receives all items and should return a [`Line`] to display, + /// or `None` to hide the preview area. + pub fn on_preview(mut self, callback: F) -> Self + where + F: Fn(&[MultiSelectItem]) -> Option> + Send + Sync + 'static, + { + self.preview_builder = Some(Box::new(callback)); + self + } + + /// Sets a callback invoked whenever an item's state changes. + /// + /// This includes both toggles and reordering operations. + #[allow(dead_code)] + pub fn on_change(mut self, callback: F) -> Self + where + F: Fn(&[MultiSelectItem], &AppEventSender) + Send + Sync + 'static, + { + self.on_change = Some(Box::new(callback)); + self + } + + /// Sets a callback invoked when the user confirms their selection (Enter). + /// + /// The callback receives a list of IDs for all enabled items. + pub fn on_confirm(mut self, callback: F) -> Self + where + F: Fn(&[String], &AppEventSender) + Send + Sync + 'static, + { + self.on_confirm = Some(Box::new(callback)); + self + } + + /// Sets a callback invoked when the user cancels the picker (Escape). + pub fn on_cancel(mut self, callback: F) -> Self + where + F: Fn(&AppEventSender) + Send + Sync + 'static, + { + self.on_cancel = Some(Box::new(callback)); + self + } + + /// Builds the [`MultiSelectPicker`] with all configured options. + /// + /// Initializes the filter to show all items and generates the initial + /// preview line if a preview callback was set. + pub fn build(self) -> MultiSelectPicker { + let mut header = ColumnRenderable::new(); + header.push(Line::from(self.title.bold())); + + if let Some(subtitle) = self.subtitle { + header.push(Line::from(subtitle.dim())); + } + + let instructions = if self.instructions.is_empty() { + vec![ + "Press ".into(), + key_hint::plain(KeyCode::Char(' ')).into(), + " to toggle; ".into(), + key_hint::plain(KeyCode::Enter).into(), + " to confirm and close; ".into(), + key_hint::plain(KeyCode::Esc).into(), + " to close".into(), + ] + } else { + self.instructions + }; + + let mut view = MultiSelectPicker { + items: self.items, + state: ScrollState::new(), + complete: false, + app_event_tx: self.app_event_tx, + header: Box::new(header), + footer_hint: Line::from(instructions), + ordering_enabled: self.ordering_enabled, + search_query: String::new(), + filtered_indices: Vec::new(), + preview_builder: self.preview_builder, + preview_line: None, + on_change: self.on_change, + on_confirm: self.on_confirm, + on_cancel: self.on_cancel, + }; + view.apply_filter(); + view.update_preview_line(); + view + } +} + +/// Performs fuzzy matching on an item against a filter string. +/// +/// Tries to match against the display name first, then falls back to name if different. Returns +/// the matching character indices (if matched on display name) and a score for sorting. +/// +/// # Arguments +/// +/// * `filter` - The search query to match against +/// * `display_name` - The primary name to match (shown to user) +/// * `name` - A secondary/canonical name to try if display name doesn't match +/// +/// # Returns +/// +/// * `Some((Some(indices), score))` - Matched on display name with highlight indices +/// * `Some((None, score))` - Matched on skill name only (no highlights for display) +/// * `None` - No match +pub(crate) fn match_item( + filter: &str, + display_name: &str, + name: &str, +) -> Option<(Option>, i32)> { + if let Some((indices, score)) = fuzzy_match(display_name, filter) { + return Some((Some(indices), score)); + } + if display_name != name + && let Some((_indices, score)) = fuzzy_match(name, filter) + { + return Some((None, score)); + } + None +} diff --git a/codex-rs/tui/src/bottom_pane/request_user_input/mod.rs b/codex-rs/tui/src/bottom_pane/request_user_input/mod.rs index 59807a8a4c..9542772446 100644 --- a/codex-rs/tui/src/bottom_pane/request_user_input/mod.rs +++ b/codex-rs/tui/src/bottom_pane/request_user_input/mod.rs @@ -427,8 +427,8 @@ impl RequestUserInputOverlay { if self.selected_option_index().is_some() && !notes_visible { tips.push(FooterTip::highlighted("tab to add notes")); } - if self.selected_option_index().is_some() && notes_visible && self.focus_is_notes() { - tips.push(FooterTip::new("tab to clear notes")); + if self.selected_option_index().is_some() && notes_visible { + tips.push(FooterTip::new("tab or esc to clear notes")); } } @@ -449,7 +449,9 @@ impl RequestUserInputOverlay { tips.push(FooterTip::new("ctrl + n next question")); } } - tips.push(FooterTip::new("esc to interrupt")); + if !(self.has_options() && notes_visible) { + tips.push(FooterTip::new("esc to interrupt")); + } tips } @@ -663,6 +665,23 @@ impl RequestUserInputOverlay { self.sync_composer_placeholder(); } + fn clear_notes_and_focus_options(&mut self) { + if !self.has_options() { + return; + } + if let Some(answer) = self.current_answer_mut() { + answer.draft = ComposerDraft::default(); + answer.answer_committed = false; + answer.notes_visible = false; + } + self.pending_submission_draft = None; + self.composer + .set_text_content(String::new(), Vec::new(), Vec::new()); + self.composer.move_cursor_to_end(); + self.focus = Focus::Options; + self.sync_composer_placeholder(); + } + /// Ensure there is a selection before allowing notes entry. fn ensure_selected_for_notes(&mut self) { if let Some(answer) = self.current_answer_mut() { @@ -976,6 +995,10 @@ impl BottomPaneView for RequestUserInputOverlay { } if matches!(key_event.code, KeyCode::Esc) { + if self.has_options() && self.notes_ui_visible() { + self.clear_notes_and_focus_options(); + return; + } // TODO: Emit interrupted request_user_input results (including committed answers) // once core supports persisting them reliably without follow-up turn issues. self.app_event_tx.send(AppEvent::CodexOp(Op::Interrupt)); @@ -1093,16 +1116,7 @@ impl BottomPaneView for RequestUserInputOverlay { Focus::Notes => { let notes_empty = self.composer.current_text_with_pending().trim().is_empty(); if self.has_options() && matches!(key_event.code, KeyCode::Tab) { - if let Some(answer) = self.current_answer_mut() { - answer.draft = ComposerDraft::default(); - answer.answer_committed = false; - answer.notes_visible = false; - } - self.composer - .set_text_content(String::new(), Vec::new(), Vec::new()); - self.composer.move_cursor_to_end(); - self.focus = Focus::Options; - self.sync_composer_placeholder(); + self.clear_notes_and_focus_options(); return; } if self.has_options() && matches!(key_event.code, KeyCode::Backspace) && notes_empty @@ -1753,7 +1767,7 @@ mod tests { } #[test] - fn esc_in_notes_mode_interrupts() { + fn esc_in_notes_mode_clears_notes_and_hides_ui() { let (tx, mut rx) = test_sender(); let mut overlay = RequestUserInputOverlay::new( request_event("turn-1", vec![question_with_options("q1", "Pick one")]), @@ -1769,12 +1783,19 @@ mod tests { overlay.handle_key_event(KeyEvent::from(KeyCode::Tab)); overlay.handle_key_event(KeyEvent::from(KeyCode::Esc)); - assert_eq!(overlay.done, true); - expect_interrupt_only(&mut rx); + let answer = overlay.current_answer().expect("answer missing"); + assert_eq!(overlay.done, false); + assert!(matches!(overlay.focus, Focus::Options)); + assert_eq!(overlay.notes_ui_visible(), false); + assert_eq!(overlay.composer.current_text_with_pending(), ""); + assert_eq!(answer.draft.text, ""); + assert_eq!(answer.options_state.selected_idx, Some(0)); + assert_eq!(answer.answer_committed, false); + assert!(rx.try_recv().is_err()); } #[test] - fn esc_in_notes_mode_interrupts_with_notes_visible() { + fn esc_in_notes_mode_with_text_clears_notes_and_hides_ui() { let (tx, mut rx) = test_sender(); let mut overlay = RequestUserInputOverlay::new( request_event("turn-1", vec![question_with_options("q1", "Pick one")]), @@ -1791,8 +1812,15 @@ mod tests { overlay.handle_key_event(KeyEvent::from(KeyCode::Char('a'))); overlay.handle_key_event(KeyEvent::from(KeyCode::Esc)); - assert_eq!(overlay.done, true); - expect_interrupt_only(&mut rx); + let answer = overlay.current_answer().expect("answer missing"); + assert_eq!(overlay.done, false); + assert!(matches!(overlay.focus, Focus::Options)); + assert_eq!(overlay.notes_ui_visible(), false); + assert_eq!(overlay.composer.current_text_with_pending(), ""); + assert_eq!(answer.draft.text, ""); + assert_eq!(answer.options_state.selected_idx, Some(0)); + assert_eq!(answer.answer_committed, false); + assert!(rx.try_recv().is_err()); } #[test] diff --git a/codex-rs/tui/src/bottom_pane/request_user_input/snapshots/codex_tui__bottom_pane__request_user_input__tests__request_user_input_options_notes_visible.snap b/codex-rs/tui/src/bottom_pane/request_user_input/snapshots/codex_tui__bottom_pane__request_user_input__tests__request_user_input_options_notes_visible.snap index d9d219b627..a4540a2b26 100644 --- a/codex-rs/tui/src/bottom_pane/request_user_input/snapshots/codex_tui__bottom_pane__request_user_input__tests__request_user_input_options_notes_visible.snap +++ b/codex-rs/tui/src/bottom_pane/request_user_input/snapshots/codex_tui__bottom_pane__request_user_input__tests__request_user_input_options_notes_visible.snap @@ -1,5 +1,6 @@ --- source: tui/src/bottom_pane/request_user_input/mod.rs +assertion_line: 2321 expression: "render_snapshot(&overlay, area)" --- @@ -16,4 +17,4 @@ expression: "render_snapshot(&overlay, area)" - tab to clear notes | enter to submit answer | esc to interrupt + tab or esc to clear notes | enter to submit answer diff --git a/codex-rs/tui/src/bottom_pane/selection_popup_common.rs b/codex-rs/tui/src/bottom_pane/selection_popup_common.rs index 89ef8b3b3f..3f827dc41f 100644 --- a/codex-rs/tui/src/bottom_pane/selection_popup_common.rs +++ b/codex-rs/tui/src/bottom_pane/selection_popup_common.rs @@ -19,7 +19,11 @@ use crate::style::user_message_style; use super::scroll_state::ScrollState; -/// A generic representation of a display row for selection popups. +/// Render-ready representation of one row in a selection popup. +/// +/// This type contains presentation-focused fields that are intentionally more +/// concrete than source domain models. `match_indices` are character offsets +/// into `name`, and `wrap_indent` is interpreted in terminal cell columns. #[derive(Default)] pub(crate) struct GenericDisplayRow { pub name: String, @@ -31,6 +35,25 @@ pub(crate) struct GenericDisplayRow { pub wrap_indent: Option, // optional indent for wrapped lines } +/// Controls how selection rows choose the split between left/right name/description columns. +/// +/// Callers should use the same mode for both measurement and rendering, or the +/// popup can reserve the wrong number of lines and clip content. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[cfg_attr(not(test), allow(dead_code))] +pub(crate) enum ColumnWidthMode { + /// Derive column placement from only the visible viewport rows. + #[default] + AutoVisible, + /// Derive column placement from all rows so scrolling does not shift columns. + AutoAllRows, + /// Use a fixed two-column split: 30% left (name), 70% right (description). + Fixed, +} + +const FIXED_LEFT_COLUMN_NUMERATOR: usize = 3; +const FIXED_LEFT_COLUMN_DENOMINATOR: usize = 10; + const MENU_SURFACE_INSET_V: u16 = 1; const MENU_SURFACE_INSET_H: u16 = 2; @@ -50,7 +73,8 @@ pub(crate) const fn menu_surface_padding_height() -> u16 { /// Paint the shared menu background and return the inset content area. /// /// This keeps the surface treatment consistent across selection-style overlays -/// (for example `/model`, approvals, and request-user-input). +/// (for example `/model`, approvals, and request-user-input). Callers should +/// render all inner content in the returned rect, not the original area. pub(crate) fn render_menu_surface(area: Rect, buf: &mut Buffer) -> Rect { if area.is_empty() { return area; @@ -61,6 +85,10 @@ pub(crate) fn render_menu_surface(area: Rect, buf: &mut Buffer) -> Rect { menu_surface_inset(area) } +/// Wrap a styled line while preserving span styles. +/// +/// The function clamps `width` to at least one terminal cell so callers can use +/// it safely with narrow layouts. pub(crate) fn wrap_styled_line<'a>(line: &'a Line<'a>, width: u16) -> Vec> { use crate::wrapping::RtOptions; use crate::wrapping::word_wrap_line; @@ -78,15 +106,20 @@ fn line_width(line: &Line<'_>) -> usize { .sum() } -fn truncate_line_to_width(line: Line<'static>, max_width: usize) -> Line<'static> { +pub(crate) fn truncate_line_to_width(line: Line<'static>, max_width: usize) -> Line<'static> { if max_width == 0 { return Line::from(Vec::>::new()); } + let Line { + style, + alignment, + spans, + } = line; let mut used = 0usize; let mut spans_out: Vec> = Vec::new(); - for span in line.spans { + for span in spans { let text = span.content.into_owned(); let style = span.style; let span_width = UnicodeWidthStr::width(text.as_str()); @@ -123,10 +156,17 @@ fn truncate_line_to_width(line: Line<'static>, max_width: usize) -> Line<'static break; } - Line::from(spans_out) + Line { + style, + alignment, + spans: spans_out, + } } -fn truncate_line_with_ellipsis_if_overflow(line: Line<'static>, max_width: usize) -> Line<'static> { +pub(crate) fn truncate_line_with_ellipsis_if_overflow( + line: Line<'static>, + max_width: usize, +) -> Line<'static> { if max_width == 0 { return Line::from(Vec::>::new()); } @@ -137,40 +177,74 @@ fn truncate_line_with_ellipsis_if_overflow(line: Line<'static>, max_width: usize } let truncated = truncate_line_to_width(line, max_width.saturating_sub(1)); - let mut spans = truncated.spans; + let Line { + style, + alignment, + mut spans, + } = truncated; let ellipsis_style = spans.last().map(|span| span.style).unwrap_or_default(); spans.push(Span::styled("…", ellipsis_style)); - Line::from(spans) + Line { + style, + alignment, + spans, + } } -/// Compute a shared description-column start based on the widest visible name -/// plus two spaces of padding. Ensures at least one column is left for the -/// description. +/// Computes the shared start column used for descriptions in selection rows. +/// The column is derived from the widest row name plus two spaces of padding +/// while always leaving at least one terminal cell for description content. +/// [`ColumnWidthMode::AutoAllRows`] computes width across the full dataset so +/// the description column does not shift as the user scrolls. fn compute_desc_col( rows_all: &[GenericDisplayRow], start_idx: usize, visible_items: usize, content_width: u16, + col_width_mode: ColumnWidthMode, ) -> usize { - let visible_range = start_idx..(start_idx + visible_items); - let max_name_width = rows_all - .iter() - .enumerate() - .filter(|(i, _)| visible_range.contains(i)) - .map(|(_, r)| { - let mut spans: Vec = vec![r.name.clone().into()]; - if r.disabled_reason.is_some() { - spans.push(" (disabled)".dim()); - } - Line::from(spans).width() - }) - .max() - .unwrap_or(0); - let mut desc_col = max_name_width.saturating_add(2); - if (desc_col as u16) >= content_width { - desc_col = content_width.saturating_sub(1) as usize; + if content_width <= 1 { + return 0; + } + + let max_desc_col = content_width.saturating_sub(1) as usize; + match col_width_mode { + ColumnWidthMode::Fixed => ((content_width as usize * FIXED_LEFT_COLUMN_NUMERATOR) + / FIXED_LEFT_COLUMN_DENOMINATOR) + .clamp(1, max_desc_col), + ColumnWidthMode::AutoVisible | ColumnWidthMode::AutoAllRows => { + let max_name_width = match col_width_mode { + ColumnWidthMode::AutoVisible => rows_all + .iter() + .enumerate() + .skip(start_idx) + .take(visible_items) + .map(|(_, row)| { + let mut spans: Vec = vec![row.name.clone().into()]; + if row.disabled_reason.is_some() { + spans.push(" (disabled)".dim()); + } + Line::from(spans).width() + }) + .max() + .unwrap_or(0), + ColumnWidthMode::AutoAllRows => rows_all + .iter() + .map(|row| { + let mut spans: Vec = vec![row.name.clone().into()]; + if row.disabled_reason.is_some() { + spans.push(" (disabled)".dim()); + } + Line::from(spans).width() + }) + .max() + .unwrap_or(0), + ColumnWidthMode::Fixed => 0, + }; + + max_name_width.saturating_add(2).min(max_desc_col) + } } - desc_col } /// Determine how many spaces to indent wrapped lines for a row. @@ -268,13 +342,14 @@ fn build_full_line(row: &GenericDisplayRow, desc_col: usize) -> Line<'static> { /// Render a list of rows using the provided ScrollState, with shared styling /// and behavior for selection popups. -pub(crate) fn render_rows( +fn render_rows_inner( area: Rect, buf: &mut Buffer, rows_all: &[GenericDisplayRow], state: &ScrollState, max_results: usize, empty_message: &str, + col_width_mode: ColumnWidthMode, ) { if rows_all.is_empty() { if area.height > 0 { @@ -301,7 +376,13 @@ pub(crate) fn render_rows( } } - let desc_col = compute_desc_col(rows_all, start_idx, visible_items, area.width); + let desc_col = compute_desc_col( + rows_all, + start_idx, + visible_items, + area.width, + col_width_mode, + ); // Render items, wrapping descriptions and aligning wrapped lines under the // shared description column. Stop when we run out of vertical space. @@ -358,7 +439,88 @@ pub(crate) fn render_rows( } } +/// Render a list of rows using the provided ScrollState, with shared styling +/// and behavior for selection popups. +/// Description alignment is computed from visible rows only, which allows the +/// layout to adapt tightly to the current viewport. +/// +/// This function should be paired with [`measure_rows_height`] when reserving +/// space; pairing it with a different measurement mode can cause clipping. +pub(crate) fn render_rows( + area: Rect, + buf: &mut Buffer, + rows_all: &[GenericDisplayRow], + state: &ScrollState, + max_results: usize, + empty_message: &str, +) { + render_rows_inner( + area, + buf, + rows_all, + state, + max_results, + empty_message, + ColumnWidthMode::AutoVisible, + ); +} + +/// Render a list of rows using the provided ScrollState, with shared styling +/// and behavior for selection popups. +/// This mode keeps column placement stable while scrolling by sizing the +/// description column against the full dataset. +/// +/// This function should be paired with +/// [`measure_rows_height_stable_col_widths`] so reserved and rendered heights +/// stay in sync. +pub(crate) fn render_rows_stable_col_widths( + area: Rect, + buf: &mut Buffer, + rows_all: &[GenericDisplayRow], + state: &ScrollState, + max_results: usize, + empty_message: &str, +) { + render_rows_inner( + area, + buf, + rows_all, + state, + max_results, + empty_message, + ColumnWidthMode::AutoAllRows, + ); +} + +/// Render a list of rows using the provided ScrollState and explicit +/// [`ColumnWidthMode`] behavior. +/// +/// This is the low-level entry point for callers that need to thread a mode +/// through higher-level configuration. +pub(crate) fn render_rows_with_col_width_mode( + area: Rect, + buf: &mut Buffer, + rows_all: &[GenericDisplayRow], + state: &ScrollState, + max_results: usize, + empty_message: &str, + col_width_mode: ColumnWidthMode, +) { + render_rows_inner( + area, + buf, + rows_all, + state, + max_results, + empty_message, + col_width_mode, + ); +} + /// Render rows as a single line each (no wrapping), truncating overflow with an ellipsis. +/// +/// This path always uses viewport-local width alignment and is best for dense +/// list UIs where multi-line descriptions would add too much vertical churn. pub(crate) fn render_rows_single_line( area: Rect, buf: &mut Buffer, @@ -390,7 +552,13 @@ pub(crate) fn render_rows_single_line( } } - let desc_col = compute_desc_col(rows_all, start_idx, visible_items, area.width); + let desc_col = compute_desc_col( + rows_all, + start_idx, + visible_items, + area.width, + ColumnWidthMode::AutoVisible, + ); let mut cur_y = area.y; for (i, row) in rows_all @@ -433,11 +601,62 @@ pub(crate) fn render_rows_single_line( /// items from `rows_all` given the current scroll/selection state and the /// available `width`. Accounts for description wrapping and alignment so the /// caller can allocate sufficient vertical space. +/// +/// This function matches [`render_rows`] semantics (`AutoVisible` column +/// sizing). Mixing it with stable or fixed render modes can under- or +/// over-estimate required height. pub(crate) fn measure_rows_height( rows_all: &[GenericDisplayRow], state: &ScrollState, max_results: usize, width: u16, +) -> u16 { + measure_rows_height_inner( + rows_all, + state, + max_results, + width, + ColumnWidthMode::AutoVisible, + ) +} + +/// Measures selection-row height while using full-dataset column alignment. +/// This should be paired with [`render_rows_stable_col_widths`] so layout +/// reservation matches rendering behavior. +pub(crate) fn measure_rows_height_stable_col_widths( + rows_all: &[GenericDisplayRow], + state: &ScrollState, + max_results: usize, + width: u16, +) -> u16 { + measure_rows_height_inner( + rows_all, + state, + max_results, + width, + ColumnWidthMode::AutoAllRows, + ) +} + +/// Measure selection-row height using explicit [`ColumnWidthMode`] behavior. +/// +/// This is the low-level companion to [`render_rows_with_col_width_mode`]. +pub(crate) fn measure_rows_height_with_col_width_mode( + rows_all: &[GenericDisplayRow], + state: &ScrollState, + max_results: usize, + width: u16, + col_width_mode: ColumnWidthMode, +) -> u16 { + measure_rows_height_inner(rows_all, state, max_results, width, col_width_mode) +} + +fn measure_rows_height_inner( + rows_all: &[GenericDisplayRow], + state: &ScrollState, + max_results: usize, + width: u16, + col_width_mode: ColumnWidthMode, ) -> u16 { if rows_all.is_empty() { return 1; // placeholder "no matches" line @@ -458,7 +677,13 @@ pub(crate) fn measure_rows_height( } } - let desc_col = compute_desc_col(rows_all, start_idx, visible_items, content_width); + let desc_col = compute_desc_col( + rows_all, + start_idx, + visible_items, + content_width, + col_width_mode, + ); use crate::wrapping::RtOptions; use crate::wrapping::word_wrap_line; diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_disabled_context_right.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_disabled_context_right.snap new file mode 100644 index 0000000000..b86792ac77 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_disabled_context_right.snap @@ -0,0 +1,5 @@ +--- +source: tui/src/bottom_pane/footer.rs +expression: terminal.backend() +--- +" ? for shortcuts · Plan mode (shift+tab to cycle) 50% context left " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_enabled_mode_right.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_enabled_mode_right.snap new file mode 100644 index 0000000000..2da49eeb64 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_enabled_mode_right.snap @@ -0,0 +1,5 @@ +--- +source: tui/src/bottom_pane/footer.rs +expression: terminal.backend() +--- +" Plan mode (shift+tab to cycle) " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_enabled_no_mode_right.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_enabled_no_mode_right.snap new file mode 100644 index 0000000000..6813891613 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_enabled_no_mode_right.snap @@ -0,0 +1,5 @@ +--- +source: tui/src/bottom_pane/footer.rs +expression: terminal.backend() +--- +" " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_overrides_context.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_overrides_context.snap new file mode 100644 index 0000000000..d3958253e3 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_overrides_context.snap @@ -0,0 +1,5 @@ +--- +source: tui/src/bottom_pane/footer.rs +expression: terminal.backend() +--- +" Italic text " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_overrides_shortcuts.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_overrides_shortcuts.snap new file mode 100644 index 0000000000..bb0e2d33b9 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_overrides_shortcuts.snap @@ -0,0 +1,5 @@ +--- +source: tui/src/bottom_pane/footer.rs +expression: terminal.backend() +--- +" Status line content " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_truncated_with_gap.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_truncated_with_gap.snap new file mode 100644 index 0000000000..cef1531fd6 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_status_line_truncated_with_gap.snap @@ -0,0 +1,5 @@ +--- +source: tui/src/bottom_pane/footer.rs +expression: terminal.backend() +--- +" Status line content that … Plan mode " diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__list_selection_view__tests__list_selection_col_width_mode_auto_all_rows_scroll.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__list_selection_view__tests__list_selection_col_width_mode_auto_all_rows_scroll.snap new file mode 100644 index 0000000000..6aaf439a9f --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__list_selection_view__tests__list_selection_col_width_mode_auto_all_rows_scroll.snap @@ -0,0 +1,31 @@ +--- +source: tui/src/bottom_pane/list_selection_view.rs +assertion_line: 1054 +expression: "render_before_after_scroll_snapshot(ColumnWidthMode::AutoAllRows, 96)" +--- +before scroll: + + Debug + +› 1. Item 1 desc 1 + 2. Item 2 desc 2 + 3. Item 3 desc 3 + 4. Item 4 desc 4 + 5. Item 5 desc 5 + 6. Item 6 desc 6 + 7. Item 7 desc 7 + 8. Item 8 desc 8 + + +after scroll: + + Debug + + 2. Item 2 desc 2 + 3. Item 3 desc 3 + 4. Item 4 desc 4 + 5. Item 5 desc 5 + 6. Item 6 desc 6 + 7. Item 7 desc 7 + 8. Item 8 desc 8 +› 9. Item 9 with an intentionally much longer name desc 9 diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__list_selection_view__tests__list_selection_col_width_mode_auto_visible_scroll.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__list_selection_view__tests__list_selection_col_width_mode_auto_visible_scroll.snap new file mode 100644 index 0000000000..6875fb5433 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__list_selection_view__tests__list_selection_col_width_mode_auto_visible_scroll.snap @@ -0,0 +1,31 @@ +--- +source: tui/src/bottom_pane/list_selection_view.rs +assertion_line: 1046 +expression: "render_before_after_scroll_snapshot(ColumnWidthMode::AutoVisible, 96)" +--- +before scroll: + + Debug + +› 1. Item 1 desc 1 + 2. Item 2 desc 2 + 3. Item 3 desc 3 + 4. Item 4 desc 4 + 5. Item 5 desc 5 + 6. Item 6 desc 6 + 7. Item 7 desc 7 + 8. Item 8 desc 8 + + +after scroll: + + Debug + + 2. Item 2 desc 2 + 3. Item 3 desc 3 + 4. Item 4 desc 4 + 5. Item 5 desc 5 + 6. Item 6 desc 6 + 7. Item 7 desc 7 + 8. Item 8 desc 8 +› 9. Item 9 with an intentionally much longer name desc 9 diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__list_selection_view__tests__list_selection_col_width_mode_fixed_scroll.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__list_selection_view__tests__list_selection_col_width_mode_fixed_scroll.snap new file mode 100644 index 0000000000..4672ab7f27 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__list_selection_view__tests__list_selection_col_width_mode_fixed_scroll.snap @@ -0,0 +1,31 @@ +--- +source: tui/src/bottom_pane/list_selection_view.rs +assertion_line: 1062 +expression: "render_before_after_scroll_snapshot(ColumnWidthMode::Fixed, 96)" +--- +before scroll: + + Debug + +› 1. Item 1 desc 1 + 2. Item 2 desc 2 + 3. Item 3 desc 3 + 4. Item 4 desc 4 + 5. Item 5 desc 5 + 6. Item 6 desc 6 + 7. Item 7 desc 7 + 8. Item 8 desc 8 + + +after scroll: + + Debug + + 2. Item 2 desc 2 + 3. Item 3 desc 3 + 4. Item 4 desc 4 + 5. Item 5 desc 5 + 6. Item 6 desc 6 + 7. Item 7 desc 7 + 8. Item 8 desc 8 +› 9. Item 9 with an intent… desc 9 diff --git a/codex-rs/tui/src/bottom_pane/status_line_setup.rs b/codex-rs/tui/src/bottom_pane/status_line_setup.rs new file mode 100644 index 0000000000..29bcf7b9c9 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/status_line_setup.rs @@ -0,0 +1,278 @@ +//! Status line configuration view for customizing the TUI status bar. +//! +//! This module provides an interactive picker for selecting which items appear +//! in the status line at the bottom of the terminal. Users can: +//! +//! - **Select items**: Toggle which information is displayed +//! - **Reorder items**: Use left/right arrows to change display order +//! - **Preview changes**: See a live preview of the configured status line +//! +//! # Available Status Line Items +//! +//! - Model information (name, reasoning level) +//! - Directory paths (current dir, project root) +//! - Git information (branch name) +//! - Context usage (remaining %, used %, window size) +//! - Usage limits (5-hour, weekly) +//! - Session info (ID, tokens used) +//! - Application version + +use ratatui::buffer::Buffer; +use ratatui::layout::Rect; +use ratatui::text::Line; +use std::collections::HashSet; +use strum::IntoEnumIterator; +use strum_macros::Display; +use strum_macros::EnumIter; +use strum_macros::EnumString; + +use crate::app_event::AppEvent; +use crate::app_event_sender::AppEventSender; +use crate::bottom_pane::CancellationEvent; +use crate::bottom_pane::bottom_pane_view::BottomPaneView; +use crate::bottom_pane::multi_select_picker::MultiSelectItem; +use crate::bottom_pane::multi_select_picker::MultiSelectPicker; +use crate::render::renderable::Renderable; + +/// Available items that can be displayed in the status line. +/// +/// Each variant represents a piece of information that can be shown at the +/// bottom of the TUI. Items are serialized to kebab-case for configuration +/// storage (e.g., `ModelWithReasoning` becomes `model-with-reasoning`). +/// +/// Some items are conditionally displayed based on availability: +/// - Git-related items only show when in a git repository +/// - Context/limit items only show when data is available from the API +/// - Session ID only shows after a session has started +#[derive(EnumIter, EnumString, Display, Debug, Clone, Eq, PartialEq)] +#[strum(serialize_all = "kebab_case")] +pub(crate) enum StatusLineItem { + /// The current model name. + ModelName, + + /// Model name with reasoning level suffix. + ModelWithReasoning, + + /// Current working directory path. + CurrentDir, + + /// Project root directory (if detected). + ProjectRoot, + + /// Current git branch name (if in a repository). + GitBranch, + + /// Percentage of context window remaining. + ContextRemaining, + + /// Percentage of context window used. + ContextUsed, + + /// Remaining usage on the 5-hour rate limit. + FiveHourLimit, + + /// Remaining usage on the weekly rate limit. + WeeklyLimit, + + /// Codex application version. + CodexVersion, + + /// Total context window size in tokens. + ContextWindowSize, + + /// Total tokens used in the current session. + UsedTokens, + + /// Total input tokens consumed. + TotalInputTokens, + + /// Total output tokens generated. + TotalOutputTokens, + + /// Full session UUID. + SessionId, +} + +impl StatusLineItem { + /// User-visible description shown in the popup. + pub(crate) fn description(&self) -> &'static str { + match self { + StatusLineItem::ModelName => "Current model name", + StatusLineItem::ModelWithReasoning => "Current model name with reasoning level", + StatusLineItem::CurrentDir => "Current working directory", + StatusLineItem::ProjectRoot => "Project root directory (omitted when unavailable)", + StatusLineItem::GitBranch => "Current Git branch (omitted when unavailable)", + StatusLineItem::ContextRemaining => { + "Percentage of context window remaining (omitted when unknown)" + } + StatusLineItem::ContextUsed => { + "Percentage of context window used (omitted when unknown)" + } + StatusLineItem::FiveHourLimit => { + "Remaining usage on 5-hour usage limit (omitted when unavailable)" + } + StatusLineItem::WeeklyLimit => { + "Remaining usage on weekly usage limit (omitted when unavailable)" + } + StatusLineItem::CodexVersion => "Codex application version", + StatusLineItem::ContextWindowSize => { + "Total context window size in tokens (omitted when unknown)" + } + StatusLineItem::UsedTokens => "Total tokens used in session (omitted when zero)", + StatusLineItem::TotalInputTokens => "Total input tokens used in session", + StatusLineItem::TotalOutputTokens => "Total output tokens used in session", + StatusLineItem::SessionId => { + "Current session identifier (omitted until session starts)" + } + } + } + + /// Returns an example rendering of this item for the preview. + /// + /// These are placeholder values used to show users what each item looks + /// like in the status line before they confirm their selection. + pub(crate) fn render(&self) -> &'static str { + match self { + StatusLineItem::ModelName => "gpt-5.2-codex", + StatusLineItem::ModelWithReasoning => "gpt-5.2-codex medium", + StatusLineItem::CurrentDir => "~/project/path", + StatusLineItem::ProjectRoot => "~/project", + StatusLineItem::GitBranch => "feat/awesome-feature", + StatusLineItem::ContextRemaining => "18% left", + StatusLineItem::ContextUsed => "82% used", + StatusLineItem::FiveHourLimit => "5h 100%", + StatusLineItem::WeeklyLimit => "weekly 98%", + StatusLineItem::CodexVersion => "v0.93.0", + StatusLineItem::ContextWindowSize => "258K window", + StatusLineItem::UsedTokens => "27.3K used", + StatusLineItem::TotalInputTokens => "17,588 in", + StatusLineItem::TotalOutputTokens => "265 out", + StatusLineItem::SessionId => "019c19bd-ceb6-73b0-adc8-8ec0397b85cf", + } + } +} + +/// Interactive view for configuring which items appear in the status line. +/// +/// Wraps a [`MultiSelectPicker`] with status-line-specific behavior: +/// - Pre-populates items from current configuration +/// - Shows a live preview of the configured status line +/// - Emits [`AppEvent::StatusLineSetup`] on confirmation +/// - Emits [`AppEvent::StatusLineSetupCancelled`] on cancellation +pub(crate) struct StatusLineSetupView { + /// The underlying multi-select picker widget. + picker: MultiSelectPicker, +} + +impl StatusLineSetupView { + /// Creates a new status line setup view. + /// + /// # Arguments + /// + /// * `status_line_items` - Currently configured item IDs (in display order), + /// or `None` to start with all items disabled + /// * `app_event_tx` - Event sender for dispatching configuration changes + /// + /// Items from `status_line_items` are shown first (in order) and marked as + /// enabled. Remaining items are appended and marked as disabled. + pub(crate) fn new(status_line_items: Option<&[String]>, app_event_tx: AppEventSender) -> Self { + let mut used_ids = HashSet::new(); + let mut items = Vec::new(); + + if let Some(selected_items) = status_line_items.as_ref() { + for id in *selected_items { + let Ok(item) = id.parse::() else { + continue; + }; + let item_id = item.to_string(); + if !used_ids.insert(item_id.clone()) { + continue; + } + items.push(Self::status_line_select_item(item, true)); + } + } + + for item in StatusLineItem::iter() { + let item_id = item.to_string(); + if used_ids.contains(&item_id) { + continue; + } + items.push(Self::status_line_select_item(item, false)); + } + + Self { + picker: MultiSelectPicker::builder( + "Configure Status Line".to_string(), + Some("Select which items to display in the status line.".to_string()), + app_event_tx, + ) + .instructions(vec![ + "Use ↑↓ to navigate, ←→ to move, space to select, enter to confirm, esc to cancel." + .into(), + ]) + .items(items) + .enable_ordering() + .on_preview(|items| { + let preview = items + .iter() + .filter(|item| item.enabled) + .filter_map(|item| item.id.parse::().ok()) + .map(|item| item.render()) + .collect::>() + .join(" · "); + if preview.is_empty() { + None + } else { + Some(Line::from(preview)) + } + }) + .on_confirm(|ids, app_event| { + let items = ids + .iter() + .map(|id| id.parse::()) + .collect::, _>>() + .unwrap_or_default(); + app_event.send(AppEvent::StatusLineSetup { items }); + }) + .on_cancel(|app_event| { + app_event.send(AppEvent::StatusLineSetupCancelled); + }) + .build(), + } + } + + /// Converts a [`StatusLineItem`] into a [`MultiSelectItem`] for the picker. + fn status_line_select_item(item: StatusLineItem, enabled: bool) -> MultiSelectItem { + MultiSelectItem { + id: item.to_string(), + name: item.to_string(), + description: Some(item.description().to_string()), + enabled, + } + } +} + +impl BottomPaneView for StatusLineSetupView { + fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) { + self.picker.handle_key_event(key_event); + } + + fn is_complete(&self) -> bool { + self.picker.complete + } + + fn on_ctrl_c(&mut self) -> CancellationEvent { + self.picker.close(); + CancellationEvent::Handled + } +} + +impl Renderable for StatusLineSetupView { + fn render(&self, area: Rect, buf: &mut Buffer) { + self.picker.render(area, buf) + } + + fn desired_height(&self, width: u16) -> u16 { + self.picker.desired_height(width) + } +} diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 40247c35a4..39e757fa78 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -26,18 +26,30 @@ use std::collections::VecDeque; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; use std::time::Duration; use std::time::Instant; +use crate::bottom_pane::StatusLineItem; +use crate::bottom_pane::StatusLineSetupView; +use crate::status::RateLimitWindowDisplay; +use crate::status::format_directory_display; +use crate::status::format_tokens_compact; +use crate::text_formatting::proper_join; use crate::version::CODEX_CLI_VERSION; +use codex_app_server_protocol::ConfigLayerSource; use codex_backend_client::Client as BackendClient; use codex_chatgpt::connectors; use codex_core::config::Config; use codex_core::config::ConstraintResult; use codex_core::config::types::Notifications; +use codex_core::config_loader::ConfigLayerStackOrdering; use codex_core::features::FEATURES; use codex_core::features::Feature; +use codex_core::find_thread_name_by_id; use codex_core::git_info::current_branch_name; +use codex_core::git_info::get_git_repo_root; use codex_core::git_info::local_git_branches; use codex_core::models_manager::manager::ModelsManager; use codex_core::project_doc::DEFAULT_PROJECT_DOC_FILENAME; @@ -93,6 +105,7 @@ use codex_core::skills::model::SkillMetadata; #[cfg(target_os = "windows")] use codex_core::windows_sandbox::WindowsSandboxLevelExt; use codex_otel::OtelManager; +use codex_otel::RuntimeMetricsSummary; use codex_protocol::ThreadId; use codex_protocol::account::PlanType; use codex_protocol::approvals::ElicitationRequestEvent; @@ -144,6 +157,7 @@ use crate::bottom_pane::BottomPane; use crate::bottom_pane::BottomPaneParams; use crate::bottom_pane::CancellationEvent; use crate::bottom_pane::CollaborationModeIndicator; +use crate::bottom_pane::ColumnWidthMode; use crate::bottom_pane::DOUBLE_PRESS_QUIT_SHORTCUT_ENABLED; use crate::bottom_pane::ExperimentalFeatureItem; use crate::bottom_pane::ExperimentalFeaturesView; @@ -196,6 +210,9 @@ mod skills; use self::skills::collect_tool_mentions; use self::skills::find_app_mentions; use self::skills::find_skill_mentions_with_tool_mentions; +use crate::streaming::chunking::AdaptiveChunkingPolicy; +use crate::streaming::commit_tick::CommitTickScope; +use crate::streaming::commit_tick::run_commit_tick; use crate::streaming::controller::PlanStreamController; use crate::streaming::controller::StreamController; @@ -385,6 +402,8 @@ pub(crate) struct ChatWidgetInit { pub(crate) is_first_run: bool, pub(crate) feedback_audience: FeedbackAudience, pub(crate) model: Option, + // Shared latch so we only warn once about invalid status-line item IDs. + pub(crate) status_line_invalid_items_warned: Arc, pub(crate) otel_manager: OtelManager, } @@ -485,6 +504,7 @@ pub(crate) struct ChatWidget { rate_limit_warnings: RateLimitWarningState, rate_limit_switch_prompt: RateLimitSwitchPromptState, rate_limit_poller: Option>, + adaptive_chunking: AdaptiveChunkingPolicy, // Stream lifecycle controller stream_controller: Option, // Stream lifecycle controller for proposed plan output. @@ -568,13 +588,26 @@ pub(crate) struct ChatWidget { // This lets the separator show per-chunk work time (since the previous separator) rather than // the total task-running time reported by the status indicator. last_separator_elapsed_secs: Option, - + // Runtime metrics accumulated across delta snapshots for the active turn. + turn_runtime_metrics: RuntimeMetricsSummary, last_rendered_width: std::cell::Cell>, // Feedback sink for /feedback feedback: codex_feedback::CodexFeedback, feedback_audience: FeedbackAudience, // Current session rollout path (if known) current_rollout_path: Option, + // Current working directory (if known) + current_cwd: Option, + // Shared latch so we only warn once about invalid status-line item IDs. + status_line_invalid_items_warned: Arc, + // Cached git branch name for the status line (None if unknown). + status_line_branch: Option, + // CWD used to resolve the cached branch; change resets branch state. + status_line_branch_cwd: Option, + // True while an async branch lookup is in flight. + status_line_branch_pending: bool, + // True once we've attempted a branch lookup for the current CWD. + status_line_branch_lookup_complete: bool, external_editor_state: ExternalEditorState, } @@ -769,6 +802,7 @@ impl ChatWidget { { self.add_boxed_history(cell); } + self.adaptive_chunking.reset(); } /// Update the status indicator header and details. @@ -785,6 +819,146 @@ impl ChatWidget { self.set_status(header, None); } + /// Sets the currently rendered footer status-line value and schedules a redraw. + pub(crate) fn set_status_line(&mut self, status_line: Option>) { + self.bottom_pane.set_status_line(status_line); + self.request_redraw(); + } + + /// Recomputes footer status-line content from config and current runtime state. + /// + /// This method is the status-line orchestrator: it parses configured item identifiers, + /// warns once per session about invalid items, updates whether status-line mode is enabled, + /// schedules async git-branch lookup when needed, and renders only values that are currently + /// available. + /// + /// The omission behavior is intentional. If selected items are unavailable (for example before + /// a session id exists or before branch lookup completes), those items are skipped without + /// placeholders so the line remains compact and stable. + pub(crate) fn refresh_status_line(&mut self) { + let (items, invalid_items) = self.status_line_items_with_invalids(); + if self.thread_id.is_some() + && !invalid_items.is_empty() + && self + .status_line_invalid_items_warned + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + let label = if invalid_items.len() == 1 { + "item" + } else { + "items" + }; + let message = format!( + "Ignored invalid status line {label}: {}.", + proper_join(invalid_items.as_slice()) + ); + self.on_warning(message); + } + if !items.contains(&StatusLineItem::GitBranch) { + self.status_line_branch = None; + self.status_line_branch_pending = false; + self.status_line_branch_lookup_complete = false; + } + let enabled = !items.is_empty(); + self.bottom_pane.set_status_line_enabled(enabled); + if !enabled { + self.set_status_line(None); + return; + } + + let cwd = self.status_line_cwd().to_path_buf(); + self.sync_status_line_branch_state(&cwd); + + if items.contains(&StatusLineItem::GitBranch) && !self.status_line_branch_lookup_complete { + self.request_status_line_branch(cwd); + } + + let mut parts = Vec::new(); + for item in items { + if let Some(value) = self.status_line_value_for_item(&item) { + parts.push(value); + } + } + + let line = if parts.is_empty() { + None + } else { + Some(Line::from(parts.join(" · "))) + }; + self.set_status_line(line); + } + + /// Records that status-line setup was canceled. + /// + /// Cancellation is intentionally side-effect free for config state; the existing configuration + /// remains active and no persistence is attempted. + pub(crate) fn cancel_status_line_setup(&self) { + tracing::info!("Status line setup canceled by user"); + } + + /// Applies status-line item selection from the setup view to in-memory config. + /// + /// An empty selection is normalized to `None` so the status line is fully disabled and the + /// behavior matches an unset `tui.status_line` config value. + pub(crate) fn setup_status_line(&mut self, items: Vec) { + tracing::info!("status line setup confirmed with items: {items:#?}"); + let ids = items.iter().map(ToString::to_string).collect::>(); + self.config.tui_status_line = if ids.is_empty() { None } else { Some(ids) }; + self.refresh_status_line(); + } + + /// Stores async git-branch lookup results for the current status-line cwd. + /// + /// Results are dropped when they target an out-of-date cwd to avoid rendering stale branch + /// names after directory changes. + pub(crate) fn set_status_line_branch(&mut self, cwd: PathBuf, branch: Option) { + if self.status_line_branch_cwd.as_ref() != Some(&cwd) { + self.status_line_branch_pending = false; + return; + } + self.status_line_branch = branch; + self.status_line_branch_pending = false; + self.status_line_branch_lookup_complete = true; + } + + /// Forces a new git-branch lookup when `GitBranch` is part of the configured status line. + fn request_status_line_branch_refresh(&mut self) { + let (items, _) = self.status_line_items_with_invalids(); + if items.is_empty() || !items.contains(&StatusLineItem::GitBranch) { + return; + } + let cwd = self.status_line_cwd().to_path_buf(); + self.sync_status_line_branch_state(&cwd); + self.request_status_line_branch(cwd); + } + + fn collect_runtime_metrics_delta(&mut self) { + if let Some(delta) = self.otel_manager.runtime_metrics_summary() { + self.apply_runtime_metrics_delta(delta); + } + } + + fn apply_runtime_metrics_delta(&mut self, delta: RuntimeMetricsSummary) { + let should_log_timing = has_websocket_timing_metrics(delta); + self.turn_runtime_metrics.merge(delta); + if should_log_timing { + self.log_websocket_timing_totals(delta); + } + } + + fn log_websocket_timing_totals(&mut self, delta: RuntimeMetricsSummary) { + if let Some(label) = history_cell::runtime_metrics_label(delta.responses_api_summary()) { + self.add_plain_history_lines(vec![ + vec!["• ".dim(), format!("WebSocket timing: {label}").dark_gray()].into(), + ]); + } + } + + fn refresh_runtime_metrics(&mut self) { + self.collect_runtime_metrics_delta(); + } + fn restore_retry_status_header_if_present(&mut self) { if let Some(header) = self.retry_status_header.take() { self.set_status_header(header); @@ -801,7 +975,9 @@ impl ChatWidget { self.thread_name = event.thread_name.clone(); self.forked_from = event.forked_from_id; self.current_rollout_path = event.rollout_path.clone(); + self.current_cwd = Some(event.cwd.clone()); let initial_messages = event.initial_messages.clone(); + let forked_from_id = event.forked_from_id; let model_for_header = event.model.clone(); self.session_header.set_model(&model_for_header); self.current_collaboration_mode = self.current_collaboration_mode.with_updates( @@ -837,11 +1013,58 @@ impl ChatWidget { if let Some(user_message) = self.initial_user_message.take() { self.submit_user_message(user_message); } + if let Some(forked_from_id) = forked_from_id { + self.emit_forked_thread_event(forked_from_id); + } if !self.suppress_session_configured_redraw { self.request_redraw(); } } + fn emit_forked_thread_event(&self, forked_from_id: ThreadId) { + let app_event_tx = self.app_event_tx.clone(); + let codex_home = self.config.codex_home.clone(); + tokio::spawn(async move { + let forked_from_id_text = forked_from_id.to_string(); + let send_name_and_id = |name: String| { + let line: Line<'static> = vec![ + "• ".dim(), + "Thread forked from ".into(), + name.cyan(), + " (".into(), + forked_from_id_text.clone().cyan(), + ")".into(), + ] + .into(); + app_event_tx.send(AppEvent::InsertHistoryCell(Box::new( + PlainHistoryCell::new(vec![line]), + ))); + }; + let send_id_only = || { + let line: Line<'static> = vec![ + "• ".dim(), + "Thread forked from ".into(), + forked_from_id_text.clone().cyan(), + ] + .into(); + app_event_tx.send(AppEvent::InsertHistoryCell(Box::new( + PlainHistoryCell::new(vec![line]), + ))); + }; + + match find_thread_name_by_id(&codex_home, &forked_from_id).await { + Ok(Some(name)) if !name.trim().is_empty() => { + send_name_and_id(name); + } + Ok(_) => send_id_only(), + Err(err) => { + tracing::warn!("Failed to read forked thread name: {err}"); + send_id_only(); + } + } + }); + } + fn on_thread_name_updated(&mut self, event: codex_core::protocol::ThreadNameUpdatedEvent) { if self.thread_id == Some(event.thread_id) { self.thread_name = event.thread_name; @@ -943,6 +1166,7 @@ impl ChatWidget { && controller.push(&delta) { self.app_event_tx.send(AppEvent::StartCommitAnimation); + self.run_catch_up_commit_tick(); } self.request_redraw(); } @@ -1020,7 +1244,9 @@ impl ChatWidget { self.saw_plan_item_this_turn = false; self.plan_delta_buffer.clear(); self.plan_item_active = false; + self.adaptive_chunking.reset(); self.plan_stream_controller = None; + self.turn_runtime_metrics = RuntimeMetricsSummary::default(); self.otel_manager.reset_runtime_metrics(); self.bottom_pane.clear_quit_shortcut_hint(); self.quit_shortcut_expires_at = None; @@ -1044,19 +1270,28 @@ impl ChatWidget { } self.flush_unified_exec_wait_streak(); if !from_replay { - let runtime_metrics = self.otel_manager.runtime_metrics_summary(); - if runtime_metrics.is_some() { - let elapsed_seconds = self - .bottom_pane - .status_widget() - .map(super::status_indicator_widget::StatusIndicatorWidget::elapsed_seconds); + self.collect_runtime_metrics_delta(); + let runtime_metrics = + (!self.turn_runtime_metrics.is_empty()).then_some(self.turn_runtime_metrics); + let show_work_separator = self.needs_final_message_separator && self.had_work_activity; + if show_work_separator || runtime_metrics.is_some() { + let elapsed_seconds = if show_work_separator { + self.bottom_pane + .status_widget() + .map(super::status_indicator_widget::StatusIndicatorWidget::elapsed_seconds) + .map(|current| self.worked_elapsed_from(current)) + } else { + None + }; self.add_to_history(history_cell::FinalMessageSeparator::new( elapsed_seconds, runtime_metrics, )); } + self.turn_runtime_metrics = RuntimeMetricsSummary::default(); self.needs_final_message_separator = false; self.had_work_activity = false; + self.request_status_line_branch_refresh(); } // Mark task stopped and request redraw now that all content is in history. self.agent_turn_running = false; @@ -1270,6 +1505,7 @@ impl ChatWidget { } else { self.rate_limit_snapshot = None; } + self.refresh_status_line(); } /// Finalize any active exec as failed and stop/clear agent-turn UI state. /// @@ -1286,7 +1522,10 @@ impl ChatWidget { self.last_unified_wait = None; self.unified_exec_wait_streak = None; self.clear_unified_exec_processes(); + self.adaptive_chunking.reset(); self.stream_controller = None; + self.plan_stream_controller = None; + self.request_status_line_branch_refresh(); self.maybe_show_pending_rate_limit_prompt(); } @@ -1518,6 +1757,8 @@ impl ChatWidget { fn on_exec_command_begin(&mut self, ev: ExecCommandBeginEvent) { self.flush_answer_stream_with_separator(); if is_unified_exec_source(ev.source) { + // Unified exec may be parsed as Unknown; keep the working indicator visible regardless. + self.bottom_pane.ensure_status_indicator(); self.track_unified_exec_process_begin(&ev); if !is_standard_tool_call(&ev.parsed_cmd) { return; @@ -1790,6 +2031,7 @@ impl ChatWidget { fn on_turn_diff(&mut self, unified_diff: String) { debug!("TurnDiffEvent: {unified_diff}"); + self.refresh_status_line(); } fn on_deprecation_notice(&mut self, event: DeprecationNoticeEvent) { @@ -1838,32 +2080,54 @@ impl ChatWidget { self.set_status(message, additional_details); } - /// Periodic tick to commit at most one queued line to history with a small delay, - /// animating the output. + /// Periodic tick for stream commits. In smooth mode this preserves one-line pacing, while + /// catch-up mode drains larger batches to reduce queue lag. pub(crate) fn on_commit_tick(&mut self) { - let mut has_controller = false; - let mut all_idle = true; - if let Some(controller) = self.stream_controller.as_mut() { - has_controller = true; - let (cell, is_idle) = controller.on_commit_tick(); - if let Some(cell) = cell { - self.bottom_pane.hide_status_indicator(); - self.add_boxed_history(cell); - } - all_idle &= is_idle; + self.run_commit_tick(); + } + + /// Runs a regular periodic commit tick. + fn run_commit_tick(&mut self) { + self.run_commit_tick_with_scope(CommitTickScope::AnyMode); + } + + /// Runs an opportunistic commit tick only if catch-up mode is active. + fn run_catch_up_commit_tick(&mut self) { + self.run_commit_tick_with_scope(CommitTickScope::CatchUpOnly); + } + + /// Runs a commit tick for the current stream queue snapshot. + /// + /// `scope` controls whether this call may commit in smooth mode or only when catch-up + /// is currently active. While lines are actively streaming we hide the status row to avoid + /// duplicate "in progress" affordances, but once all stream controllers go idle for this + /// turn we restore the status row if the task is still running so users keep a live + /// spinner/shimmer signal between preamble output and subsequent tool activity. + fn run_commit_tick_with_scope(&mut self, scope: CommitTickScope) { + let now = Instant::now(); + let outcome = run_commit_tick( + &mut self.adaptive_chunking, + self.stream_controller.as_mut(), + self.plan_stream_controller.as_mut(), + scope, + now, + ); + for cell in outcome.cells { + self.bottom_pane.hide_status_indicator(); + self.add_boxed_history(cell); } - if let Some(controller) = self.plan_stream_controller.as_mut() { - has_controller = true; - let (cell, is_idle) = controller.on_commit_tick(); - if let Some(cell) = cell { - self.bottom_pane.hide_status_indicator(); - self.add_boxed_history(cell); + + if outcome.has_controller && outcome.all_idle { + if self.bottom_pane.is_task_running() { + self.bottom_pane.ensure_status_indicator(); + self.set_status_header(self.current_status_header.clone()); } - all_idle &= is_idle; - } - if has_controller && all_idle { self.app_event_tx.send(AppEvent::StopCommitAnimation); } + + if self.agent_turn_running { + self.refresh_runtime_metrics(); + } } fn flush_interrupt_queue(&mut self) { @@ -1930,6 +2194,7 @@ impl ChatWidget { && controller.push(&delta) { self.app_event_tx.send(AppEvent::StartCommitAnimation); + self.run_catch_up_commit_tick(); } self.request_redraw(); } @@ -2204,6 +2469,7 @@ impl ChatWidget { is_first_run, feedback_audience, model, + status_line_invalid_items_warned, otel_manager, } = common; let model = model.filter(|m| !m.trim().is_empty()); @@ -2236,6 +2502,7 @@ impl ChatWidget { let active_cell = Some(Self::placeholder_session_header_cell(&config)); + let current_cwd = Some(config.cwd.clone()); let mut widget = Self { app_event_tx: app_event_tx.clone(), frame_requester: frame_requester.clone(), @@ -2268,6 +2535,7 @@ impl ChatWidget { rate_limit_warnings: RateLimitWarningState::default(), rate_limit_switch_prompt: RateLimitSwitchPromptState::default(), rate_limit_poller: None, + adaptive_chunking: AdaptiveChunkingPolicy::default(), stream_controller: None, plan_stream_controller: None, running_commands: HashMap::new(), @@ -2302,10 +2570,17 @@ impl ChatWidget { plan_delta_buffer: String::new(), plan_item_active: false, last_separator_elapsed_secs: None, + turn_runtime_metrics: RuntimeMetricsSummary::default(), last_rendered_width: std::cell::Cell::new(None), feedback, feedback_audience, current_rollout_path: None, + current_cwd, + status_line_invalid_items_warned, + status_line_branch: None, + status_line_branch_cwd: None, + status_line_branch_pending: false, + status_line_branch_lookup_complete: false, external_editor_state: ExternalEditorState::Closed, }; @@ -2313,6 +2588,13 @@ impl ChatWidget { widget .bottom_pane .set_steer_enabled(widget.config.features.enabled(Feature::Steer)); + widget.bottom_pane.set_status_line_enabled( + widget + .config + .tui_status_line + .as_ref() + .is_some_and(|items| !items.is_empty()), + ); widget.bottom_pane.set_collaboration_modes_enabled( widget.config.features.enabled(Feature::CollaborationModes), ); @@ -2350,6 +2632,7 @@ impl ChatWidget { is_first_run, feedback_audience, model, + status_line_invalid_items_warned, otel_manager, } = common; let model = model.filter(|m| !m.trim().is_empty()); @@ -2380,6 +2663,7 @@ impl ChatWidget { }; let active_cell = Some(Self::placeholder_session_header_cell(&config)); + let current_cwd = Some(config.cwd.clone()); let mut widget = Self { app_event_tx: app_event_tx.clone(), @@ -2413,6 +2697,7 @@ impl ChatWidget { rate_limit_warnings: RateLimitWarningState::default(), rate_limit_switch_prompt: RateLimitSwitchPromptState::default(), rate_limit_poller: None, + adaptive_chunking: AdaptiveChunkingPolicy::default(), stream_controller: None, plan_stream_controller: None, running_commands: HashMap::new(), @@ -2447,10 +2732,17 @@ impl ChatWidget { needs_final_message_separator: false, had_work_activity: false, last_separator_elapsed_secs: None, + turn_runtime_metrics: RuntimeMetricsSummary::default(), last_rendered_width: std::cell::Cell::new(None), feedback, feedback_audience, current_rollout_path: None, + current_cwd, + status_line_invalid_items_warned, + status_line_branch: None, + status_line_branch_cwd: None, + status_line_branch_pending: false, + status_line_branch_lookup_complete: false, external_editor_state: ExternalEditorState::Closed, }; @@ -2458,6 +2750,13 @@ impl ChatWidget { widget .bottom_pane .set_steer_enabled(widget.config.features.enabled(Feature::Steer)); + widget.bottom_pane.set_status_line_enabled( + widget + .config + .tui_status_line + .as_ref() + .is_some_and(|items| !items.is_empty()), + ); widget.bottom_pane.set_collaboration_modes_enabled( widget.config.features.enabled(Feature::CollaborationModes), ); @@ -2481,10 +2780,11 @@ impl ChatWidget { auth_manager, models_manager, feedback, + is_first_run: _, feedback_audience, model, + status_line_invalid_items_warned, otel_manager, - .. } = common; let model = model.filter(|m| !m.trim().is_empty()); let mut rng = rand::rng(); @@ -2501,6 +2801,7 @@ impl ChatWidget { .and_then(|mask| mask.model.clone()) .unwrap_or(header_model); + let current_cwd = Some(session_configured.cwd.clone()); let codex_op_tx = spawn_agent_from_existing(conversation, session_configured, app_event_tx.clone()); @@ -2547,6 +2848,7 @@ impl ChatWidget { rate_limit_warnings: RateLimitWarningState::default(), rate_limit_switch_prompt: RateLimitSwitchPromptState::default(), rate_limit_poller: None, + adaptive_chunking: AdaptiveChunkingPolicy::default(), stream_controller: None, plan_stream_controller: None, running_commands: HashMap::new(), @@ -2581,10 +2883,17 @@ impl ChatWidget { plan_delta_buffer: String::new(), plan_item_active: false, last_separator_elapsed_secs: None, + turn_runtime_metrics: RuntimeMetricsSummary::default(), last_rendered_width: std::cell::Cell::new(None), feedback, feedback_audience, current_rollout_path: None, + current_cwd, + status_line_invalid_items_warned, + status_line_branch: None, + status_line_branch_cwd: None, + status_line_branch_pending: false, + status_line_branch_lookup_complete: false, external_editor_state: ExternalEditorState::Closed, }; @@ -2592,6 +2901,13 @@ impl ChatWidget { widget .bottom_pane .set_steer_enabled(widget.config.features.enabled(Feature::Steer)); + widget.bottom_pane.set_status_line_enabled( + widget + .config + .tui_status_line + .as_ref() + .is_some_and(|items| !items.is_empty()), + ); widget.bottom_pane.set_collaboration_modes_enabled( widget.config.features.enabled(Feature::CollaborationModes), ); @@ -2851,6 +3167,7 @@ impl ChatWidget { self.open_review_popup(); } SlashCommand::Rename => { + self.otel_manager.counter("codex.thread.rename", 1, &[]); self.show_rename_prompt(); } SlashCommand::Model => { @@ -2981,6 +3298,12 @@ impl ChatWidget { SlashCommand::Status => { self.add_status_output(); } + SlashCommand::DebugConfig => { + self.add_debug_config_output(); + } + SlashCommand::Statusline => { + self.open_status_line_setup(); + } SlashCommand::Ps => { self.add_ps_output(); } @@ -3064,6 +3387,7 @@ impl ChatWidget { let trimmed = args.trim(); match cmd { SlashCommand::Rename if !trimmed.is_empty() => { + self.otel_manager.counter("codex.thread.rename", 1, &[]); let Some((prepared_args, _prepared_elements)) = self.bottom_pane.prepare_inline_args_submission(false) else { @@ -3579,6 +3903,10 @@ impl ChatWidget { } } } + + if !from_replay && self.agent_turn_running { + self.refresh_runtime_metrics(); + } } fn on_entered_review_mode(&mut self, review: ReviewRequest, from_replay: bool) { @@ -3756,6 +4084,233 @@ impl ChatWidget { )); } + pub(crate) fn add_debug_config_output(&mut self) { + self.add_to_history(crate::debug_config::new_debug_config_output(&self.config)); + } + + fn open_status_line_setup(&mut self) { + let view = StatusLineSetupView::new( + self.config.tui_status_line.as_deref(), + self.app_event_tx.clone(), + ); + self.bottom_pane.show_view(Box::new(view)); + } + + /// Parses configured status-line ids into known items and collects unknown ids. + /// + /// Unknown ids are deduplicated in insertion order for warning messages. + fn status_line_items_with_invalids(&self) -> (Vec, Vec) { + let mut invalid = Vec::new(); + let mut invalid_seen = HashSet::new(); + let mut items = Vec::new(); + let Some(config_items) = self.config.tui_status_line.as_ref() else { + return (items, invalid); + }; + for id in config_items { + match id.parse::() { + Ok(item) => items.push(item), + Err(_) => { + if invalid_seen.insert(id.clone()) { + invalid.push(format!(r#""{id}""#)); + } + } + } + } + (items, invalid) + } + + fn status_line_cwd(&self) -> &Path { + self.current_cwd.as_ref().unwrap_or(&self.config.cwd) + } + + fn status_line_project_root(&self) -> Option { + let cwd = self.status_line_cwd(); + if let Some(repo_root) = get_git_repo_root(cwd) { + return Some(repo_root); + } + + self.config + .config_layer_stack + .get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true) + .iter() + .find_map(|layer| match &layer.name { + ConfigLayerSource::Project { dot_codex_folder } => { + dot_codex_folder.as_path().parent().map(Path::to_path_buf) + } + _ => None, + }) + } + + fn status_line_project_root_name(&self) -> Option { + self.status_line_project_root().map(|root| { + root.file_name() + .map(|name| name.to_string_lossy().to_string()) + .unwrap_or_else(|| format_directory_display(&root, None)) + }) + } + + /// Resets git-branch cache state when the status-line cwd changes. + /// + /// The branch cache is keyed by cwd because branch lookup is performed relative to that path. + /// Keeping stale branch values across cwd changes would surface incorrect repository context. + fn sync_status_line_branch_state(&mut self, cwd: &Path) { + if self + .status_line_branch_cwd + .as_ref() + .is_some_and(|path| path == cwd) + { + return; + } + self.status_line_branch_cwd = Some(cwd.to_path_buf()); + self.status_line_branch = None; + self.status_line_branch_pending = false; + self.status_line_branch_lookup_complete = false; + } + + /// Starts an async git-branch lookup unless one is already running. + /// + /// The resulting `StatusLineBranchUpdated` event carries the lookup cwd so callers can reject + /// stale completions after directory changes. + fn request_status_line_branch(&mut self, cwd: PathBuf) { + if self.status_line_branch_pending { + return; + } + self.status_line_branch_pending = true; + let tx = self.app_event_tx.clone(); + tokio::spawn(async move { + let branch = current_branch_name(&cwd).await; + tx.send(AppEvent::StatusLineBranchUpdated { cwd, branch }); + }); + } + + /// Resolves a display string for one configured status-line item. + /// + /// Returning `None` means "omit this item for now", not "configuration error". Callers rely on + /// this to keep partially available status lines readable while waiting for session, token, or + /// git metadata. + fn status_line_value_for_item(&self, item: &StatusLineItem) -> Option { + match item { + StatusLineItem::ModelName => Some(self.model_display_name().to_string()), + StatusLineItem::ModelWithReasoning => { + let label = + Self::status_line_reasoning_effort_label(self.effective_reasoning_effort()); + Some(format!("{} {label}", self.model_display_name())) + } + StatusLineItem::CurrentDir => { + Some(format_directory_display(self.status_line_cwd(), None)) + } + StatusLineItem::ProjectRoot => self.status_line_project_root_name(), + StatusLineItem::GitBranch => self.status_line_branch.clone(), + StatusLineItem::UsedTokens => { + let usage = self.status_line_total_usage(); + let total = usage.tokens_in_context_window(); + if total <= 0 { + None + } else { + Some(format!("{} used", format_tokens_compact(total))) + } + } + StatusLineItem::ContextRemaining => self + .status_line_context_remaining_percent() + .map(|remaining| format!("{remaining}% left")), + StatusLineItem::ContextUsed => self + .status_line_context_used_percent() + .map(|used| format!("{used}% used")), + StatusLineItem::FiveHourLimit => { + let window = self + .rate_limit_snapshot + .as_ref() + .and_then(|s| s.primary.as_ref()); + let label = window + .and_then(|window| window.window_minutes) + .map(get_limits_duration) + .unwrap_or_else(|| "5h".to_string()); + self.status_line_limit_display(window, &label) + } + StatusLineItem::WeeklyLimit => { + let window = self + .rate_limit_snapshot + .as_ref() + .and_then(|s| s.secondary.as_ref()); + let label = window + .and_then(|window| window.window_minutes) + .map(get_limits_duration) + .unwrap_or_else(|| "weekly".to_string()); + self.status_line_limit_display(window, &label) + } + StatusLineItem::CodexVersion => Some(CODEX_CLI_VERSION.to_string()), + StatusLineItem::ContextWindowSize => self + .status_line_context_window_size() + .map(|cws| format!("{} window", format_tokens_compact(cws))), + StatusLineItem::TotalInputTokens => Some(format!( + "{} in", + format_tokens_compact(self.status_line_total_usage().input_tokens) + )), + StatusLineItem::TotalOutputTokens => Some(format!( + "{} out", + format_tokens_compact(self.status_line_total_usage().output_tokens) + )), + StatusLineItem::SessionId => self.thread_id.map(|id| id.to_string()), + } + } + + fn status_line_context_window_size(&self) -> Option { + self.token_info + .as_ref() + .and_then(|info| info.model_context_window) + .or(self.config.model_context_window) + } + + fn status_line_context_remaining_percent(&self) -> Option { + let Some(context_window) = self.status_line_context_window_size() else { + return Some(100); + }; + let default_usage = TokenUsage::default(); + let usage = self + .token_info + .as_ref() + .map(|info| &info.last_token_usage) + .unwrap_or(&default_usage); + Some( + usage + .percent_of_context_window_remaining(context_window) + .clamp(0, 100), + ) + } + + fn status_line_context_used_percent(&self) -> Option { + let remaining = self.status_line_context_remaining_percent().unwrap_or(100); + Some((100 - remaining).clamp(0, 100)) + } + + fn status_line_total_usage(&self) -> TokenUsage { + self.token_info + .as_ref() + .map(|info| info.total_token_usage.clone()) + .unwrap_or_default() + } + + fn status_line_limit_display( + &self, + window: Option<&RateLimitWindowDisplay>, + label: &str, + ) -> Option { + let window = window?; + let remaining = (100.0f64 - window.used_percent).clamp(0.0f64, 100.0f64); + Some(format!("{label} {remaining:.0}%")) + } + + fn status_line_reasoning_effort_label(effort: Option) -> &'static str { + match effort { + Some(ReasoningEffortConfig::Minimal) => "minimal", + Some(ReasoningEffortConfig::Low) => "low", + Some(ReasoningEffortConfig::Medium) => "medium", + Some(ReasoningEffortConfig::High) => "high", + Some(ReasoningEffortConfig::XHigh) => "xhigh", + None | Some(ReasoningEffortConfig::None) => "default", + } + } + pub(crate) fn add_ps_output(&mut self) { let processes = self .unified_exec_processes @@ -5449,11 +6004,10 @@ impl ChatWidget { if !self.collaboration_modes_enabled() { return None; } - match self.active_mode_kind() { - ModeKind::Plan => Some("Plan"), - ModeKind::Default => Some("Default"), - ModeKind::PairProgramming | ModeKind::Execute => None, - } + let active_mode = self.active_mode_kind(); + active_mode + .is_tui_visible() + .then_some(active_mode.display_name()) } fn collaboration_mode_indicator(&self) -> Option { @@ -5473,6 +6027,7 @@ impl ChatWidget { fn personality_label(personality: Personality) -> &'static str { match personality { + Personality::None => "None", Personality::Friendly => "Friendly", Personality::Pragmatic => "Pragmatic", } @@ -5480,6 +6035,7 @@ impl ChatWidget { fn personality_description(personality: Personality) -> &'static str { match personality { + Personality::None => "No personality instructions.", Personality::Friendly => "Warm, collaborative, and helpful.", Personality::Pragmatic => "Concise, task-focused, and direct.", } @@ -5718,6 +6274,7 @@ impl ChatWidget { items, is_searchable: true, search_placeholder: Some("Type to search apps".to_string()), + col_width_mode: ColumnWidthMode::AutoAllRows, ..Default::default() }); } @@ -6181,6 +6738,15 @@ impl ChatWidget { } } +fn has_websocket_timing_metrics(summary: RuntimeMetricsSummary) -> bool { + summary.responses_api_overhead_ms > 0 + || summary.responses_api_inference_time_ms > 0 + || summary.responses_api_engine_iapi_ttft_ms > 0 + || summary.responses_api_engine_service_ttft_ms > 0 + || summary.responses_api_engine_iapi_tbt_ms > 0 + || summary.responses_api_engine_service_tbt_ms > 0 +} + impl Drop for ChatWidget { fn drop(&mut self) { self.stop_rate_limit_poller(); diff --git a/codex-rs/tui/src/chatwidget/agent.rs b/codex-rs/tui/src/chatwidget/agent.rs index c902e5c638..d905f884f8 100644 --- a/codex-rs/tui/src/chatwidget/agent.rs +++ b/codex-rs/tui/src/chatwidget/agent.rs @@ -62,7 +62,13 @@ pub(crate) fn spawn_agent( }); while let Ok(event) = thread.next_event().await { + let is_shutdown_complete = matches!(event.msg, EventMsg::ShutdownComplete); app_event_tx_clone.send(AppEvent::CodexEvent(event)); + if is_shutdown_complete { + // ShutdownComplete is terminal for a thread; drop this receiver task so + // the Arc can be released and thread resources can clean up. + break; + } } }); @@ -99,7 +105,13 @@ pub(crate) fn spawn_agent_from_existing( }); while let Ok(event) = thread.next_event().await { + let is_shutdown_complete = matches!(event.msg, EventMsg::ShutdownComplete); app_event_tx_clone.send(AppEvent::CodexEvent(event)); + if is_shutdown_complete { + // ShutdownComplete is terminal for a thread; drop this receiver task so + // the Arc can be released and thread resources can clean up. + break; + } } }); diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__forked_thread_history_line.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__forked_thread_history_line.snap new file mode 100644 index 0000000000..d089f59639 --- /dev/null +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__forked_thread_history_line.snap @@ -0,0 +1,5 @@ +--- +source: tui/src/chatwidget/tests.rs +expression: combined +--- +• Thread forked from named-thread (e9f18a88-8081-4e51-9d4e-8af5cde2d8dd) diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__forked_thread_history_line_without_name.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__forked_thread_history_line_without_name.snap new file mode 100644 index 0000000000..f25eb53645 --- /dev/null +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__forked_thread_history_line_without_name.snap @@ -0,0 +1,5 @@ +--- +source: tui/src/chatwidget/tests.rs +expression: combined +--- +• Thread forked from 019c2d47-4935-7423-a190-05691f566092 diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__personality_selection_popup.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__personality_selection_popup.snap index 3c6bba94e6..3cd887f2e4 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__personality_selection_popup.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__personality_selection_popup.snap @@ -5,7 +5,7 @@ expression: popup Select Personality Choose a communication style for Codex. Disable in /experimental. -› 1. Friendly (current) Warm, collaborative, and helpful. - 2. Pragmatic Concise, task-focused, and direct. + 1. Friendly Warm, collaborative, and helpful. +› 2. Pragmatic (current) Concise, task-focused, and direct. Press enter to confirm or esc to go back diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__preamble_keeps_working_status.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__preamble_keeps_working_status.snap new file mode 100644 index 0000000000..b240e4b5f6 --- /dev/null +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__preamble_keeps_working_status.snap @@ -0,0 +1,11 @@ +--- +source: tui/src/chatwidget/tests.rs +expression: terminal.backend() +--- +" " +"• Working (0s • esc to interrupt) " +" " +" " +"› Ask Codex to do anything " +" " +" ? for shortcuts 100% context left " diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__unified_exec_begin_restores_working_status.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__unified_exec_begin_restores_working_status.snap new file mode 100644 index 0000000000..a2ab0f168a --- /dev/null +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__unified_exec_begin_restores_working_status.snap @@ -0,0 +1,12 @@ +--- +source: tui/src/chatwidget/tests.rs +expression: terminal.backend() +--- +" " +"• Working (0s • esc to interrupt) " +" 1 background terminal running · /ps to view " +" " +" " +"› Ask Codex to do anything " +" " +" ? for shortcuts 100% context left " diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index d080140a32..1b70abf975 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -62,6 +62,7 @@ use codex_core::protocol::UndoStartedEvent; use codex_core::protocol::ViewImageToolCallEvent; use codex_core::protocol::WarningEvent; use codex_otel::OtelManager; +use codex_otel::RuntimeMetricsSummary; use codex_protocol::ThreadId; use codex_protocol::account::PlanType; use codex_protocol::config_types::CollaborationMode; @@ -246,6 +247,70 @@ async fn replayed_user_message_preserves_text_elements_and_local_images() { assert_eq!(stored_images, local_images); } +#[tokio::test] +async fn forked_thread_history_line_includes_name_and_id_snapshot() { + let (chat, mut rx, _op_rx) = make_chatwidget_manual(None).await; + let mut chat = chat; + let temp = tempdir().expect("tempdir"); + chat.config.codex_home = temp.path().to_path_buf(); + + let forked_from_id = + ThreadId::from_string("e9f18a88-8081-4e51-9d4e-8af5cde2d8dd").expect("forked id"); + let session_index_entry = format!( + "{{\"id\":\"{forked_from_id}\",\"thread_name\":\"named-thread\",\"updated_at\":\"2024-01-02T00:00:00Z\"}}\n" + ); + std::fs::write(temp.path().join("session_index.jsonl"), session_index_entry) + .expect("write session index"); + + chat.emit_forked_thread_event(forked_from_id); + + let history_cell = tokio::time::timeout(std::time::Duration::from_secs(2), async { + loop { + match rx.recv().await { + Some(AppEvent::InsertHistoryCell(cell)) => break cell, + Some(_) => continue, + None => panic!("app event channel closed before forked thread history was emitted"), + } + } + }) + .await + .expect("timed out waiting for forked thread history"); + let combined = lines_to_single_string(&history_cell.display_lines(80)); + + assert!( + combined.contains("Thread forked from"), + "expected forked thread message in history" + ); + assert_snapshot!("forked_thread_history_line", combined); +} + +#[tokio::test] +async fn forked_thread_history_line_without_name_shows_id_once_snapshot() { + let (chat, mut rx, _op_rx) = make_chatwidget_manual(None).await; + let mut chat = chat; + let temp = tempdir().expect("tempdir"); + chat.config.codex_home = temp.path().to_path_buf(); + + let forked_from_id = + ThreadId::from_string("019c2d47-4935-7423-a190-05691f566092").expect("forked id"); + chat.emit_forked_thread_event(forked_from_id); + + let history_cell = tokio::time::timeout(std::time::Duration::from_secs(2), async { + loop { + match rx.recv().await { + Some(AppEvent::InsertHistoryCell(cell)) => break cell, + Some(_) => continue, + None => panic!("app event channel closed before forked thread history was emitted"), + } + } + }) + .await + .expect("timed out waiting for forked thread history"); + let combined = lines_to_single_string(&history_cell.display_lines(80)); + + assert_snapshot!("forked_thread_history_line_without_name", combined); +} + #[tokio::test] async fn submission_preserves_text_elements_and_local_images() { let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None).await; @@ -766,6 +831,7 @@ async fn helpers_are_available_and_do_not_panic() { is_first_run: true, feedback_audience: FeedbackAudience::External, model: Some(resolved_model), + status_line_invalid_items_warned: Arc::new(AtomicBool::new(false)), otel_manager, }; let mut w = ChatWidget::new(init, thread_manager); @@ -852,6 +918,7 @@ async fn make_chatwidget_manual( rate_limit_warnings: RateLimitWarningState::default(), rate_limit_switch_prompt: RateLimitSwitchPromptState::default(), rate_limit_poller: None, + adaptive_chunking: crate::streaming::chunking::AdaptiveChunkingPolicy::default(), stream_controller: None, plan_stream_controller: None, running_commands: HashMap::new(), @@ -889,10 +956,17 @@ async fn make_chatwidget_manual( plan_delta_buffer: String::new(), plan_item_active: false, last_separator_elapsed_secs: None, + turn_runtime_metrics: RuntimeMetricsSummary::default(), last_rendered_width: std::cell::Cell::new(None), feedback: codex_feedback::CodexFeedback::new(), feedback_audience: FeedbackAudience::External, current_rollout_path: None, + current_cwd: None, + status_line_invalid_items_warned: Arc::new(AtomicBool::new(false)), + status_line_branch: None, + status_line_branch_cwd: None, + status_line_branch_pending: false, + status_line_branch_lookup_complete: false, external_editor_state: ExternalEditorState::Closed, }; widget.set_model(&resolved_model); @@ -1809,7 +1883,7 @@ async fn streaming_final_answer_keeps_task_running_state() { chat.on_commit_tick(); assert!(chat.bottom_pane.is_task_running()); - assert!(chat.bottom_pane.status_widget().is_none()); + assert!(chat.bottom_pane.status_widget().is_some()); chat.bottom_pane .set_composer_text("queued submission".to_string(), Vec::new(), Vec::new()); @@ -1831,7 +1905,7 @@ async fn streaming_final_answer_keeps_task_running_state() { } #[tokio::test] -async fn exec_begin_restores_status_indicator_after_preamble() { +async fn preamble_keeps_status_indicator_visible_until_exec_begin() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await; chat.on_task_started(); @@ -1841,7 +1915,7 @@ async fn exec_begin_restores_status_indicator_after_preamble() { chat.on_commit_tick(); drain_insert_history(&mut rx); - assert_eq!(chat.bottom_pane.status_indicator_visible(), false); + assert_eq!(chat.bottom_pane.status_indicator_visible(), true); assert_eq!(chat.bottom_pane.is_task_running(), true); begin_exec(&mut chat, "call-1", "echo hi"); @@ -1849,6 +1923,69 @@ async fn exec_begin_restores_status_indicator_after_preamble() { assert_eq!(chat.bottom_pane.status_indicator_visible(), true); } +#[tokio::test] +async fn preamble_keeps_working_status_snapshot() { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await; + chat.thread_id = Some(ThreadId::new()); + + // Regression sequence: a preamble line is committed to history before any exec/tool event. + // The status row must remain visible so the spinner/shimmer still communicates "working". + chat.on_task_started(); + chat.on_agent_message_delta("Preamble line\n".to_string()); + chat.on_commit_tick(); + drain_insert_history(&mut rx); + + let height = chat.desired_height(80); + let mut terminal = ratatui::Terminal::new(ratatui::backend::TestBackend::new(80, height)) + .expect("create terminal"); + terminal + .draw(|f| chat.render(f.area(), f.buffer_mut())) + .expect("draw preamble + status widget"); + assert_snapshot!("preamble_keeps_working_status", terminal.backend()); +} + +#[tokio::test] +async fn unified_exec_begin_restores_status_indicator_after_preamble() { + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await; + + chat.on_task_started(); + assert_eq!(chat.bottom_pane.status_indicator_visible(), true); + + // Simulate a hidden status row during an active turn. + chat.bottom_pane.hide_status_indicator(); + assert_eq!(chat.bottom_pane.status_indicator_visible(), false); + assert_eq!(chat.bottom_pane.is_task_running(), true); + + begin_unified_exec_startup(&mut chat, "call-1", "proc-1", "sleep 2"); + + assert_eq!(chat.bottom_pane.status_indicator_visible(), true); +} + +#[tokio::test] +async fn unified_exec_begin_restores_working_status_snapshot() { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await; + + chat.on_task_started(); + chat.on_agent_message_delta("Preamble line\n".to_string()); + chat.on_commit_tick(); + drain_insert_history(&mut rx); + + begin_unified_exec_startup(&mut chat, "call-1", "proc-1", "sleep 2"); + + let width: u16 = 80; + let height = chat.desired_height(width); + let mut terminal = ratatui::Terminal::new(ratatui::backend::TestBackend::new(width, height)) + .expect("create terminal"); + terminal.set_viewport_area(Rect::new(0, 0, width, height)); + terminal + .draw(|f| chat.render(f.area(), f.buffer_mut())) + .expect("draw chatwidget"); + assert_snapshot!( + "unified_exec_begin_restores_working_status", + terminal.backend() + ); +} + #[tokio::test] async fn ctrl_c_shutdown_works_with_caps_lock() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await; @@ -2509,6 +2646,7 @@ async fn collaboration_modes_defaults_to_code_on_startup() { is_first_run: true, feedback_audience: FeedbackAudience::External, model: Some(resolved_model.clone()), + status_line_invalid_items_warned: Arc::new(AtomicBool::new(false)), otel_manager, }; @@ -2554,6 +2692,7 @@ async fn experimental_mode_plan_applies_on_startup() { is_first_run: true, feedback_audience: FeedbackAudience::External, model: Some(resolved_model.clone()), + status_line_invalid_items_warned: Arc::new(AtomicBool::new(false)), otel_manager, }; @@ -4779,6 +4918,83 @@ async fn warning_event_adds_warning_history_cell() { ); } +#[tokio::test] +async fn status_line_invalid_items_warn_once() { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await; + chat.config.tui_status_line = Some(vec![ + "model_name".to_string(), + "bogus_item".to_string(), + "lines_changed".to_string(), + "bogus_item".to_string(), + ]); + chat.thread_id = Some(ThreadId::new()); + + chat.refresh_status_line(); + let cells = drain_insert_history(&mut rx); + assert_eq!(cells.len(), 1, "expected one warning history cell"); + let rendered = lines_to_single_string(&cells[0]); + assert!( + rendered.contains("bogus_item"), + "warning cell missing invalid item content: {rendered}" + ); + + chat.refresh_status_line(); + let cells = drain_insert_history(&mut rx); + assert!( + cells.is_empty(), + "expected invalid status line warning to emit only once" + ); +} + +#[tokio::test] +async fn status_line_branch_state_resets_when_git_branch_disabled() { + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await; + chat.status_line_branch = Some("main".to_string()); + chat.status_line_branch_pending = true; + chat.status_line_branch_lookup_complete = true; + chat.config.tui_status_line = Some(vec!["model_name".to_string()]); + + chat.refresh_status_line(); + + assert_eq!(chat.status_line_branch, None); + assert!(!chat.status_line_branch_pending); + assert!(!chat.status_line_branch_lookup_complete); +} + +#[tokio::test] +async fn status_line_branch_refreshes_after_turn_complete() { + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await; + chat.config.tui_status_line = Some(vec!["git-branch".to_string()]); + chat.status_line_branch_lookup_complete = true; + chat.status_line_branch_pending = false; + + chat.handle_codex_event(Event { + id: "turn-1".into(), + msg: EventMsg::TurnComplete(TurnCompleteEvent { + last_agent_message: None, + }), + }); + + assert!(chat.status_line_branch_pending); +} + +#[tokio::test] +async fn status_line_branch_refreshes_after_interrupt() { + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await; + chat.config.tui_status_line = Some(vec!["git-branch".to_string()]); + chat.status_line_branch_lookup_complete = true; + chat.status_line_branch_pending = false; + + chat.handle_codex_event(Event { + id: "turn-1".into(), + msg: EventMsg::TurnAborted(codex_core::protocol::TurnAbortedEvent { + reason: TurnAbortReason::Interrupted, + }), + }); + + assert!(chat.status_line_branch_pending); +} + #[tokio::test] async fn stream_recovery_restores_previous_status_header() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await; @@ -4815,6 +5031,50 @@ async fn stream_recovery_restores_previous_status_header() { assert!(chat.retry_status_header.is_none()); } +#[tokio::test] +async fn runtime_metrics_websocket_timing_logs_and_final_separator_sums_totals() { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await; + chat.set_feature_enabled(Feature::RuntimeMetrics, true); + + chat.on_task_started(); + chat.apply_runtime_metrics_delta(RuntimeMetricsSummary { + responses_api_engine_iapi_ttft_ms: 120, + responses_api_engine_service_tbt_ms: 50, + ..RuntimeMetricsSummary::default() + }); + + let first_log = drain_insert_history(&mut rx) + .iter() + .map(|lines| lines_to_single_string(lines)) + .find(|line| line.contains("WebSocket timing:")) + .expect("expected websocket timing log"); + assert!(first_log.contains("TTFT: 120ms (iapi)")); + assert!(first_log.contains("TBT: 50ms (service)")); + + chat.apply_runtime_metrics_delta(RuntimeMetricsSummary { + responses_api_engine_iapi_ttft_ms: 80, + ..RuntimeMetricsSummary::default() + }); + + let second_log = drain_insert_history(&mut rx) + .iter() + .map(|lines| lines_to_single_string(lines)) + .find(|line| line.contains("WebSocket timing:")) + .expect("expected websocket timing log"); + assert!(second_log.contains("TTFT: 80ms (iapi)")); + + chat.on_task_complete(None, false); + let mut final_separator = None; + while let Ok(event) = rx.try_recv() { + if let AppEvent::InsertHistoryCell(cell) = event { + final_separator = Some(lines_to_single_string(&cell.display_lines(300))); + } + } + let final_separator = final_separator.expect("expected final separator with runtime metrics"); + assert!(final_separator.contains("TTFT: 80ms (iapi)")); + assert!(final_separator.contains("TBT: 50ms (service)")); +} + #[tokio::test] async fn multiple_agent_messages_in_single_turn_emit_multiple_headers() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await; diff --git a/codex-rs/tui/src/collaboration_modes.rs b/codex-rs/tui/src/collaboration_modes.rs index 3595cf5691..1e5676ccde 100644 --- a/codex-rs/tui/src/collaboration_modes.rs +++ b/codex-rs/tui/src/collaboration_modes.rs @@ -2,15 +2,11 @@ use codex_core::models_manager::manager::ModelsManager; use codex_protocol::config_types::CollaborationModeMask; use codex_protocol::config_types::ModeKind; -fn is_tui_mode(kind: ModeKind) -> bool { - matches!(kind, ModeKind::Plan | ModeKind::Default) -} - fn filtered_presets(models_manager: &ModelsManager) -> Vec { models_manager .list_collaboration_modes() .into_iter() - .filter(|mask| mask.mode.is_some_and(is_tui_mode)) + .filter(|mask| mask.mode.is_some_and(ModeKind::is_tui_visible)) .collect() } @@ -31,7 +27,7 @@ pub(crate) fn mask_for_kind( models_manager: &ModelsManager, kind: ModeKind, ) -> Option { - if !is_tui_mode(kind) { + if !kind.is_tui_visible() { return None; } filtered_presets(models_manager) diff --git a/codex-rs/tui/src/debug_config.rs b/codex-rs/tui/src/debug_config.rs new file mode 100644 index 0000000000..4202264fb2 --- /dev/null +++ b/codex-rs/tui/src/debug_config.rs @@ -0,0 +1,338 @@ +use crate::history_cell::PlainHistoryCell; +use codex_app_server_protocol::ConfigLayerSource; +use codex_core::config::Config; +use codex_core::config_loader::ConfigLayerStack; +use codex_core::config_loader::ConfigLayerStackOrdering; +use codex_core::config_loader::RequirementSource; +use codex_core::config_loader::ResidencyRequirement; +use codex_core::config_loader::SandboxModeRequirement; +use ratatui::style::Stylize; +use ratatui::text::Line; + +pub(crate) fn new_debug_config_output(config: &Config) -> PlainHistoryCell { + PlainHistoryCell::new(render_debug_config_lines(&config.config_layer_stack)) +} + +fn render_debug_config_lines(stack: &ConfigLayerStack) -> Vec> { + let mut lines = vec!["/debug-config".magenta().into(), "".into()]; + + lines.push( + "Config layer stack (lowest precedence first):" + .bold() + .into(), + ); + let layers = stack.get_layers(ConfigLayerStackOrdering::LowestPrecedenceFirst, true); + if layers.is_empty() { + lines.push(" ".dim().into()); + } else { + for (index, layer) in layers.iter().enumerate() { + let source = format_config_layer_source(&layer.name); + let status = if layer.is_disabled() { + "disabled" + } else { + "enabled" + }; + lines.push(format!(" {}. {source} ({status})", index + 1).into()); + if let Some(reason) = &layer.disabled_reason { + lines.push(format!(" reason: {reason}").dim().into()); + } + } + } + + let requirements = stack.requirements(); + let requirements_toml = stack.requirements_toml(); + + lines.push("".into()); + lines.push("Requirements:".bold().into()); + let mut requirement_lines = Vec::new(); + + if let Some(policies) = requirements_toml.allowed_approval_policies.as_ref() { + let value = join_or_empty(policies.iter().map(ToString::to_string).collect::>()); + requirement_lines.push(requirement_line( + "allowed_approval_policies", + value, + requirements.approval_policy.source.as_ref(), + )); + } + + if let Some(modes) = requirements_toml.allowed_sandbox_modes.as_ref() { + let value = join_or_empty( + modes + .iter() + .copied() + .map(format_sandbox_mode_requirement) + .collect::>(), + ); + requirement_lines.push(requirement_line( + "allowed_sandbox_modes", + value, + requirements.sandbox_policy.source.as_ref(), + )); + } + + if let Some(servers) = requirements_toml.mcp_servers.as_ref() { + let value = join_or_empty(servers.keys().cloned().collect::>()); + requirement_lines.push(requirement_line( + "mcp_servers", + value, + requirements + .mcp_servers + .as_ref() + .map(|sourced| &sourced.source), + )); + } + + // TODO(gt): Expand this debug output with detailed skills and rules display. + if requirements_toml.rules.is_some() { + requirement_lines.push(requirement_line( + "rules", + "configured".to_string(), + requirements.exec_policy_source(), + )); + } + + if let Some(residency) = requirements_toml.enforce_residency { + requirement_lines.push(requirement_line( + "enforce_residency", + format_residency_requirement(residency), + requirements.enforce_residency.source.as_ref(), + )); + } + + if requirement_lines.is_empty() { + lines.push(" ".dim().into()); + } else { + lines.extend(requirement_lines); + } + + lines +} + +fn requirement_line( + name: &str, + value: String, + source: Option<&RequirementSource>, +) -> Line<'static> { + let source = source + .map(ToString::to_string) + .unwrap_or_else(|| "".to_string()); + format!(" - {name}: {value} (source: {source})").into() +} + +fn join_or_empty(values: Vec) -> String { + if values.is_empty() { + "".to_string() + } else { + values.join(", ") + } +} + +fn format_config_layer_source(source: &ConfigLayerSource) -> String { + match source { + ConfigLayerSource::Mdm { domain, key } => { + format!("mdm ({domain}:{key})") + } + ConfigLayerSource::System { file } => { + format!("system ({})", file.as_path().display()) + } + ConfigLayerSource::User { file } => { + format!("user ({})", file.as_path().display()) + } + ConfigLayerSource::Project { dot_codex_folder } => { + format!( + "project ({}/config.toml)", + dot_codex_folder.as_path().display() + ) + } + ConfigLayerSource::SessionFlags => "session-flags".to_string(), + ConfigLayerSource::LegacyManagedConfigTomlFromFile { file } => { + format!("legacy managed_config.toml ({})", file.as_path().display()) + } + ConfigLayerSource::LegacyManagedConfigTomlFromMdm => { + "legacy managed_config.toml (mdm)".to_string() + } + } +} + +fn format_sandbox_mode_requirement(mode: SandboxModeRequirement) -> String { + match mode { + SandboxModeRequirement::ReadOnly => "read-only".to_string(), + SandboxModeRequirement::WorkspaceWrite => "workspace-write".to_string(), + SandboxModeRequirement::DangerFullAccess => "danger-full-access".to_string(), + SandboxModeRequirement::ExternalSandbox => "external-sandbox".to_string(), + } +} + +fn format_residency_requirement(requirement: ResidencyRequirement) -> String { + match requirement { + ResidencyRequirement::Us => "us".to_string(), + } +} + +#[cfg(test)] +mod tests { + use super::render_debug_config_lines; + use codex_app_server_protocol::ConfigLayerSource; + use codex_core::config::Constrained; + use codex_core::config_loader::ConfigLayerEntry; + use codex_core::config_loader::ConfigLayerStack; + use codex_core::config_loader::ConfigRequirements; + use codex_core::config_loader::ConfigRequirementsToml; + use codex_core::config_loader::ConstrainedWithSource; + use codex_core::config_loader::McpServerIdentity; + use codex_core::config_loader::McpServerRequirement; + use codex_core::config_loader::RequirementSource; + use codex_core::config_loader::ResidencyRequirement; + use codex_core::config_loader::SandboxModeRequirement; + use codex_core::config_loader::Sourced; + use codex_core::protocol::AskForApproval; + use codex_core::protocol::SandboxPolicy; + use codex_utils_absolute_path::AbsolutePathBuf; + use ratatui::text::Line; + use std::collections::BTreeMap; + use toml::Value as TomlValue; + + fn empty_toml_table() -> TomlValue { + TomlValue::Table(toml::map::Map::new()) + } + + fn absolute_path(path: &str) -> AbsolutePathBuf { + AbsolutePathBuf::from_absolute_path(path).expect("absolute path") + } + + fn render_to_text(lines: &[Line<'static>]) -> String { + lines + .iter() + .map(|line| { + line.spans + .iter() + .map(|span| span.content.as_ref()) + .collect::() + }) + .collect::>() + .join("\n") + } + + #[test] + fn debug_config_output_lists_all_layers_including_disabled() { + let system_file = if cfg!(windows) { + absolute_path("C:\\etc\\codex\\config.toml") + } else { + absolute_path("/etc/codex/config.toml") + }; + let project_folder = if cfg!(windows) { + absolute_path("C:\\repo\\.codex") + } else { + absolute_path("/repo/.codex") + }; + + let layers = vec![ + ConfigLayerEntry::new( + ConfigLayerSource::System { file: system_file }, + empty_toml_table(), + ), + ConfigLayerEntry::new_disabled( + ConfigLayerSource::Project { + dot_codex_folder: project_folder, + }, + empty_toml_table(), + "project is untrusted", + ), + ]; + let stack = ConfigLayerStack::new( + layers, + ConfigRequirements::default(), + ConfigRequirementsToml::default(), + ) + .expect("config layer stack"); + + let rendered = render_to_text(&render_debug_config_lines(&stack)); + assert!(rendered.contains("(enabled)")); + assert!(rendered.contains("(disabled)")); + assert!(rendered.contains("reason: project is untrusted")); + assert!(rendered.contains("Requirements:")); + assert!(rendered.contains(" ")); + } + + #[test] + fn debug_config_output_lists_requirement_sources() { + let requirements_file = if cfg!(windows) { + absolute_path("C:\\etc\\codex\\requirements.toml") + } else { + absolute_path("/etc/codex/requirements.toml") + }; + let mut requirements = ConfigRequirements::default(); + requirements.approval_policy = ConstrainedWithSource::new( + Constrained::allow_any(AskForApproval::OnRequest), + Some(RequirementSource::CloudRequirements), + ); + requirements.sandbox_policy = ConstrainedWithSource::new( + Constrained::allow_any(SandboxPolicy::ReadOnly), + Some(RequirementSource::SystemRequirementsToml { + file: requirements_file.clone(), + }), + ); + requirements.mcp_servers = Some(Sourced::new( + BTreeMap::from([( + "docs".to_string(), + McpServerRequirement { + identity: McpServerIdentity::Command { + command: "codex-mcp".to_string(), + }, + }, + )]), + RequirementSource::LegacyManagedConfigTomlFromMdm, + )); + requirements.enforce_residency = ConstrainedWithSource::new( + Constrained::allow_any(Some(ResidencyRequirement::Us)), + Some(RequirementSource::CloudRequirements), + ); + + let requirements_toml = ConfigRequirementsToml { + allowed_approval_policies: Some(vec![AskForApproval::OnRequest]), + allowed_sandbox_modes: Some(vec![SandboxModeRequirement::ReadOnly]), + mcp_servers: Some(BTreeMap::from([( + "docs".to_string(), + McpServerRequirement { + identity: McpServerIdentity::Command { + command: "codex-mcp".to_string(), + }, + }, + )])), + rules: None, + enforce_residency: Some(ResidencyRequirement::Us), + }; + + let user_file = if cfg!(windows) { + absolute_path("C:\\users\\alice\\.codex\\config.toml") + } else { + absolute_path("/home/alice/.codex/config.toml") + }; + let stack = ConfigLayerStack::new( + vec![ConfigLayerEntry::new( + ConfigLayerSource::User { file: user_file }, + empty_toml_table(), + )], + requirements, + requirements_toml, + ) + .expect("config layer stack"); + + let rendered = render_to_text(&render_debug_config_lines(&stack)); + assert!( + rendered.contains("allowed_approval_policies: on-request (source: cloud requirements)") + ); + assert!( + rendered.contains( + format!( + "allowed_sandbox_modes: read-only (source: {})", + requirements_file.as_path().display() + ) + .as_str(), + ) + ); + assert!(rendered.contains("mcp_servers: docs (source: MDM managed_config.toml (legacy))")); + assert!(rendered.contains("enforce_residency: us (source: cloud requirements)")); + assert!(!rendered.contains(" - rules:")); + } +} diff --git a/codex-rs/tui/src/diff_render.rs b/codex-rs/tui/src/diff_render.rs index 505ebc8edc..9683df53ba 100644 --- a/codex-rs/tui/src/diff_render.rs +++ b/codex-rs/tui/src/diff_render.rs @@ -325,7 +325,7 @@ pub(crate) fn display_path_for(path: &Path, cwd: &Path) -> String { chosen.display().to_string() } -fn calculate_add_remove_from_diff(diff: &str) -> (usize, usize) { +pub(crate) fn calculate_add_remove_from_diff(diff: &str) -> (usize, usize) { if let Ok(patch) = diffy::Patch::from_str(diff) { patch .hunks() diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index 49ccc481bf..26a91dc6e6 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -2172,7 +2172,7 @@ impl HistoryCell for FinalMessageSeparator { } } -fn runtime_metrics_label(summary: RuntimeMetricsSummary) -> Option { +pub(crate) fn runtime_metrics_label(summary: RuntimeMetricsSummary) -> Option { let mut parts = Vec::new(); if summary.tool_calls.count > 0 { let duration = format_duration_ms(summary.tool_calls.duration_ms); @@ -2213,6 +2213,42 @@ fn runtime_metrics_label(summary: RuntimeMetricsSummary) -> Option { summary.websocket_events.count )); } + if summary.responses_api_overhead_ms > 0 { + let duration = format_duration_ms(summary.responses_api_overhead_ms); + parts.push(format!("Responses API overhead: {duration}")); + } + if summary.responses_api_inference_time_ms > 0 { + let duration = format_duration_ms(summary.responses_api_inference_time_ms); + parts.push(format!("Responses API inference: {duration}")); + } + if summary.responses_api_engine_iapi_ttft_ms > 0 + || summary.responses_api_engine_service_ttft_ms > 0 + { + let mut ttft_parts = Vec::new(); + if summary.responses_api_engine_iapi_ttft_ms > 0 { + let duration = format_duration_ms(summary.responses_api_engine_iapi_ttft_ms); + ttft_parts.push(format!("{duration} (iapi)")); + } + if summary.responses_api_engine_service_ttft_ms > 0 { + let duration = format_duration_ms(summary.responses_api_engine_service_ttft_ms); + ttft_parts.push(format!("{duration} (service)")); + } + parts.push(format!("TTFT: {}", ttft_parts.join(" "))); + } + if summary.responses_api_engine_iapi_tbt_ms > 0 + || summary.responses_api_engine_service_tbt_ms > 0 + { + let mut tbt_parts = Vec::new(); + if summary.responses_api_engine_iapi_tbt_ms > 0 { + let duration = format_duration_ms(summary.responses_api_engine_iapi_tbt_ms); + tbt_parts.push(format!("{duration} (iapi)")); + } + if summary.responses_api_engine_service_tbt_ms > 0 { + let duration = format_duration_ms(summary.responses_api_engine_service_tbt_ms); + tbt_parts.push(format!("{duration} (service)")); + } + parts.push(format!("TBT: {}", tbt_parts.join(" "))); + } if parts.is_empty() { None } else { @@ -2381,9 +2417,15 @@ mod tests { count: 4, duration_ms: 1_200, }, + responses_api_overhead_ms: 650, + responses_api_inference_time_ms: 1_940, + responses_api_engine_iapi_ttft_ms: 410, + responses_api_engine_service_ttft_ms: 460, + responses_api_engine_iapi_tbt_ms: 1_180, + responses_api_engine_service_tbt_ms: 1_240, }; let cell = FinalMessageSeparator::new(Some(12), Some(summary)); - let rendered = render_lines(&cell.display_lines(200)); + let rendered = render_lines(&cell.display_lines(600)); assert_eq!(rendered.len(), 1); assert!(!rendered[0].contains("Worked for")); @@ -2392,6 +2434,10 @@ mod tests { assert!(rendered[0].contains("WebSocket: 1 events send (700ms)")); assert!(rendered[0].contains("Streams: 6 events (900ms)")); assert!(rendered[0].contains("4 events received (1.2s)")); + assert!(rendered[0].contains("Responses API overhead: 650ms")); + assert!(rendered[0].contains("Responses API inference: 1.9s")); + assert!(rendered[0].contains("TTFT: 410ms (iapi) 460ms (service)")); + assert!(rendered[0].contains("TBT: 1.2s (iapi) 1.2s (service)")); } #[test] diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 2e5aee5c38..daabb60273 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -7,7 +7,6 @@ use additional_dirs::add_dir_warning_message; use app::App; pub use app::AppExitInfo; pub use app::ExitReason; -use codex_app_server_protocol::AuthMode; use codex_cloud_requirements::cloud_requirements_loader; use codex_common::oss::ensure_oss_provider_ready; use codex_common::oss::get_default_model_for_oss_provider; @@ -16,6 +15,7 @@ use codex_core::CodexAuth; use codex_core::INTERACTIVE_SESSION_SOURCES; use codex_core::RolloutRecorder; use codex_core::ThreadSortKey; +use codex_core::auth::AuthMode; use codex_core::auth::enforce_login_restrictions; use codex_core::config::Config; use codex_core::config::ConfigBuilder; @@ -67,6 +67,7 @@ mod collaboration_modes; mod color; pub mod custom_terminal; mod cwd_prompt; +mod debug_config; mod diff_render; mod exec_cell; mod exec_command; @@ -412,7 +413,7 @@ async fn run_ratatui_app( initial_config: Config, overrides: ConfigOverrides, cli_kv_overrides: Vec<(String, toml::Value)>, - cloud_requirements: CloudRequirementsLoader, + mut cloud_requirements: CloudRequirementsLoader, feedback: codex_feedback::CodexFeedback, ) -> color_eyre::Result { color_eyre::install()?; @@ -469,9 +470,10 @@ async fn run_ratatui_app( should_show_onboarding(login_status, &initial_config, should_show_trust_screen_flag); let config = if should_show_onboarding { + let show_login_screen = should_show_login_screen(login_status, &initial_config); let onboarding_result = run_onboarding_app( OnboardingScreenArgs { - show_login_screen: should_show_login_screen(login_status, &initial_config), + show_login_screen, show_trust_screen: should_show_trust_screen_flag, login_status, auth_manager: auth_manager.clone(), @@ -492,9 +494,19 @@ async fn run_ratatui_app( exit_reason: ExitReason::UserRequested, }); } - // If the user made an explicit trust decision, reload config so current - // process state reflects what was persisted to config.toml. - if onboarding_result.directory_trust_decision.is_some() { + // If this onboarding run included the login step, always refresh cloud requirements and + // rebuild config. This avoids missing newly available cloud requirements due to login + // status detection edge cases. + if show_login_screen { + cloud_requirements = cloud_requirements_loader( + auth_manager.clone(), + initial_config.chatgpt_base_url.clone(), + ); + } + + // If the user made an explicit trust decision, or we showed the login flow, reload config + // so current process state reflects persisted trust/auth changes. + if onboarding_result.directory_trust_decision.is_some() || show_login_screen { load_config_or_exit( cli_kv_overrides.clone(), overrides.clone(), @@ -667,6 +679,7 @@ async fn run_ratatui_app( } _ => config, }; + set_default_client_residency_requirement(config.enforce_residency.value()); let active_profile = config.active_profile.clone(); let should_show_trust_screen = should_show_trust_screen(&config); @@ -828,7 +841,7 @@ fn get_login_status(config: &Config) -> LoginStatus { // to refresh the token. Block on it. let codex_home = config.codex_home.clone(); match CodexAuth::from_auth_storage(&codex_home, config.cli_auth_credentials_store_mode) { - Ok(Some(auth)) => LoginStatus::AuthMode(auth.api_auth_mode()), + Ok(Some(auth)) => LoginStatus::AuthMode(auth.auth_mode()), Ok(None) => LoginStatus::NotAuthenticated, Err(err) => { error!("Failed to read auth.json: {err}"); diff --git a/codex-rs/tui/src/onboarding/auth.rs b/codex-rs/tui/src/onboarding/auth.rs index 7378b6628e..c639d24a08 100644 --- a/codex-rs/tui/src/onboarding/auth.rs +++ b/codex-rs/tui/src/onboarding/auth.rs @@ -30,7 +30,7 @@ use ratatui::widgets::Paragraph; use ratatui::widgets::WidgetRef; use ratatui::widgets::Wrap; -use codex_app_server_protocol::AuthMode; +use codex_core::auth::AuthMode; use codex_protocol::config_types::ForcedLoginMethod; use std::sync::RwLock; @@ -661,10 +661,7 @@ impl AuthModeWidget { } fn handle_existing_chatgpt_login(&mut self) -> bool { - if matches!( - self.login_status, - LoginStatus::AuthMode(AuthMode::Chatgpt | AuthMode::ChatgptAuthTokens) - ) { + if matches!(self.login_status, LoginStatus::AuthMode(AuthMode::Chatgpt)) { *self.sign_in_state.write().unwrap() = SignInState::ChatGptSuccess; self.request_frame.schedule_frame(); true diff --git a/codex-rs/tui/src/pager_overlay.rs b/codex-rs/tui/src/pager_overlay.rs index 5f3f79e320..58f15fa7f3 100644 --- a/codex-rs/tui/src/pager_overlay.rs +++ b/codex-rs/tui/src/pager_overlay.rs @@ -17,7 +17,6 @@ use std::io::Result; use std::sync::Arc; -use std::time::Duration; use crate::chatwidget::ActiveCellTranscriptKey; use crate::history_cell::HistoryCell; @@ -299,7 +298,7 @@ impl PagerView { } } tui.frame_requester() - .schedule_frame_in(Duration::from_millis(16)); + .schedule_frame_in(crate::tui::TARGET_FRAME_INTERVAL); Ok(()) } diff --git a/codex-rs/tui/src/resume_picker.rs b/codex-rs/tui/src/resume_picker.rs index e80c4ad793..85fdbb160e 100644 --- a/codex-rs/tui/src/resume_picker.rs +++ b/codex-rs/tui/src/resume_picker.rs @@ -14,7 +14,6 @@ use codex_core::ThreadSortKey; use codex_core::ThreadsPage; use codex_core::find_thread_names_by_ids; use codex_core::path_utils; -use codex_protocol::items::TurnItem; use color_eyre::eyre::Result; use crossterm::event::KeyCode; use crossterm::event::KeyEvent; @@ -37,8 +36,6 @@ use crate::tui::FrameRequester; use crate::tui::Tui; use crate::tui::TuiEvent; use codex_protocol::ThreadId; -use codex_protocol::models::ResponseItem; -use codex_protocol::protocol::SessionMetaLine; const PAGE_SIZE: usize = 25; const LOAD_NEAR_THRESHOLD: usize = 5; @@ -86,6 +83,7 @@ struct PageLoadRequest { request_token: usize, search_token: Option, default_provider: String, + sort_key: ThreadSortKey, } type PageLoader = Arc; @@ -99,9 +97,21 @@ enum BackgroundEvent { } /// Interactive session picker that lists recorded rollout files with simple -/// search and pagination. Shows the session name when available, otherwise the -/// first user input as the preview, relative time (e.g., "5 seconds ago"), and -/// the absolute path. +/// search and pagination. +/// +/// The picker displays sessions in a table with timestamp columns (created/updated), +/// git branch, working directory, and conversation preview. Users can toggle +/// between sorting by creation time and last-updated time using the Tab key. +/// +/// Sessions are loaded on-demand via cursor-based pagination. The backend +/// `RolloutRecorder::list_threads` returns pages ordered by the selected sort key, +/// and the picker deduplicates across pages to handle overlapping windows when +/// new sessions appear during pagination. +/// +/// Filtering happens in two layers: +/// 1. Provider and source filtering at the backend (only interactive CLI sessions +/// for the current model provider). +/// 2. Working-directory filtering at the picker (unless `--all` is passed). pub async fn run_resume_picker( tui: &mut Tui, codex_home: &Path, @@ -160,7 +170,7 @@ async fn run_session_picker( &request.codex_home, PAGE_SIZE, request.cursor.as_ref(), - ThreadSortKey::CreatedAt, + request.sort_key, INTERACTIVE_SESSION_SOURCES, Some(provider_filter.as_slice()), request.default_provider.as_str(), @@ -223,6 +233,14 @@ async fn run_session_picker( Ok(SessionSelection::StartFresh) } +/// Returns the human-readable column header for the given sort key. +fn sort_key_label(sort_key: ThreadSortKey) -> &'static str { + match sort_key { + ThreadSortKey::CreatedAt => "Created at", + ThreadSortKey::UpdatedAt => "Updated at", + } +} + /// RAII guard that ensures we leave the alt-screen on scope exit. struct AltScreenGuard<'a> { tui: &'a mut Tui, @@ -260,6 +278,7 @@ struct PickerState { show_all: bool, filter_cwd: Option, action: SessionPickerAction, + sort_key: ThreadSortKey, thread_name_cache: HashMap>, } @@ -376,6 +395,7 @@ impl PickerState { show_all, filter_cwd, action, + sort_key: ThreadSortKey::CreatedAt, thread_name_cache: HashMap::new(), } } @@ -432,6 +452,10 @@ impl PickerState { self.request_frame(); } } + KeyCode::Tab => { + self.toggle_sort_key(); + self.request_frame(); + } KeyCode::Backspace => { let mut new_query = self.query.clone(); new_query.pop(); @@ -459,13 +483,21 @@ impl PickerState { self.all_rows.clear(); self.filtered_rows.clear(); self.seen_paths.clear(); - self.search_state = SearchState::Idle; self.selected = 0; + let search_token = if self.query.is_empty() { + self.search_state = SearchState::Idle; + None + } else { + let token = self.allocate_search_token(); + self.search_state = SearchState::Active { token }; + Some(token) + }; + let request_token = self.allocate_request_token(); self.pagination.loading = LoadingState::Pending(PendingLoad { request_token, - search_token: None, + search_token, }); self.request_frame(); @@ -473,8 +505,9 @@ impl PickerState { codex_home: self.codex_home.clone(), cursor: None, request_token, - search_token: None, + search_token, default_provider: self.default_provider.clone(), + sort_key: self.sort_key, }); } @@ -745,6 +778,7 @@ impl PickerState { request_token, search_token, default_provider: self.default_provider.clone(), + sort_key: self.sort_key, }); } @@ -759,6 +793,19 @@ impl PickerState { self.next_search_token = self.next_search_token.wrapping_add(1); token } + + /// Cycles the sort order between creation time and last-updated time. + /// + /// Triggers a full reload because the backend must re-sort all sessions. + /// The existing `all_rows` are cleared and pagination restarts from the + /// beginning with the new sort key. + fn toggle_sort_key(&mut self) { + self.sort_key = match self.sort_key { + ThreadSortKey::CreatedAt => ThreadSortKey::UpdatedAt, + ThreadSortKey::UpdatedAt => ThreadSortKey::CreatedAt, + }; + self.start_initial_load(); + } } fn rows_from_items(items: Vec) -> Vec { @@ -766,49 +813,33 @@ fn rows_from_items(items: Vec) -> Vec { } fn head_to_row(item: &ThreadItem) -> Row { - let created_at = item - .created_at - .as_deref() - .and_then(parse_timestamp_str) - .or_else(|| item.head.first().and_then(extract_timestamp)); + let created_at = item.created_at.as_deref().and_then(parse_timestamp_str); let updated_at = item .updated_at .as_deref() .and_then(parse_timestamp_str) .or(created_at); - let (cwd, git_branch, thread_id) = extract_session_meta_from_head(&item.head); - let preview = preview_from_head(&item.head) - .map(|s| s.trim().to_string()) + let preview = item + .first_user_message + .as_deref() + .map(str::trim) .filter(|s| !s.is_empty()) + .map(str::to_string) .unwrap_or_else(|| String::from("(no message yet)")); Row { path: item.path.clone(), preview, - thread_id, + thread_id: item.thread_id, thread_name: None, created_at, updated_at, - cwd, - git_branch, + cwd: item.cwd.clone(), + git_branch: item.git_branch.clone(), } } -fn extract_session_meta_from_head( - head: &[serde_json::Value], -) -> (Option, Option, Option) { - for value in head { - if let Ok(meta_line) = serde_json::from_value::(value.clone()) { - let cwd = Some(meta_line.meta.cwd); - let git_branch = meta_line.git.and_then(|git| git.branch); - let thread_id = Some(meta_line.meta.id); - return (cwd, git_branch, thread_id); - } - } - (None, None, None) -} - fn paths_match(a: &Path, b: &Path) -> bool { if let (Ok(ca), Ok(cb)) = ( path_utils::normalize_for_path_comparison(a), @@ -825,23 +856,6 @@ fn parse_timestamp_str(ts: &str) -> Option> { .ok() } -fn extract_timestamp(value: &serde_json::Value) -> Option> { - value - .get("timestamp") - .and_then(|v| v.as_str()) - .and_then(|t| chrono::DateTime::parse_from_rfc3339(t).ok()) - .map(|dt| dt.with_timezone(&Utc)) -} - -fn preview_from_head(head: &[serde_json::Value]) -> Option { - head.iter() - .filter_map(|value| serde_json::from_value::(value.clone()).ok()) - .find_map(|item| match codex_core::parse_turn_item(&item) { - Some(TurnItem::UserMessage(user)) => Some(user.message()), - _ => None, - }) -} - fn draw_picker(tui: &mut Tui, state: &PickerState) -> std::io::Result<()> { // Render full-screen overlay let height = tui.terminal.size()?.height; @@ -857,7 +871,15 @@ fn draw_picker(tui: &mut Tui, state: &PickerState) -> std::io::Result<()> { .areas(area); // Header - frame.render_widget_ref(Line::from(vec![state.action.title().bold().cyan()]), header); + let header_line: Line = vec![ + state.action.title().bold().cyan(), + " ".into(), + "Sort:".dim(), + " ".into(), + sort_key_label(state.sort_key).magenta(), + ] + .into(); + frame.render_widget_ref(header_line, header); // Search line let q = if state.query.is_empty() { @@ -870,7 +892,7 @@ fn draw_picker(tui: &mut Tui, state: &PickerState) -> std::io::Result<()> { let metrics = calculate_column_metrics(&state.filtered_rows, state.show_all); // Column headers and list - render_column_headers(frame, columns, &metrics); + render_column_headers(frame, columns, &metrics, state.sort_key); render_list(frame, list, state, &metrics); // Hint line @@ -885,6 +907,9 @@ fn draw_picker(tui: &mut Tui, state: &PickerState) -> std::io::Result<()> { key_hint::ctrl(KeyCode::Char('c')).into(), " to quit ".dim(), " ".dim(), + key_hint::plain(KeyCode::Tab).into(), + " to toggle sort ".dim(), + " ".dim(), key_hint::plain(KeyCode::Up).into(), "/".dim(), key_hint::plain(KeyCode::Down).into(), @@ -918,11 +943,13 @@ fn render_list( let labels = &metrics.labels; let mut y = area.y; + let visibility = column_visibility(area.width, metrics, state.sort_key); + let max_created_width = metrics.max_created_width; let max_updated_width = metrics.max_updated_width; let max_branch_width = metrics.max_branch_width; let max_cwd_width = metrics.max_cwd_width; - for (idx, (row, (updated_label, branch_label, cwd_label))) in rows[start..end] + for (idx, (row, (created_label, updated_label, branch_label, cwd_label))) in rows[start..end] .iter() .zip(labels[start..end].iter()) .enumerate() @@ -930,12 +957,17 @@ fn render_list( let is_sel = start + idx == state.selected; let marker = if is_sel { "> ".bold() } else { " ".into() }; let marker_width = 2usize; - let updated_span = if max_updated_width == 0 { - None + let created_span = if visibility.show_created { + Some(Span::from(format!("{created_label: 0 { + if visibility.show_created { + preview_width = preview_width.saturating_sub(max_created_width + 2); + } + if visibility.show_updated { preview_width = preview_width.saturating_sub(max_updated_width + 2); } - if max_branch_width > 0 { + if visibility.show_branch { preview_width = preview_width.saturating_sub(max_branch_width + 2); } - if max_cwd_width > 0 { + if visibility.show_cwd { preview_width = preview_width.saturating_sub(max_cwd_width + 2); } - let add_leading_gap = max_updated_width == 0 && max_branch_width == 0 && max_cwd_width == 0; + let add_leading_gap = !visibility.show_created + && !visibility.show_updated + && !visibility.show_branch + && !visibility.show_cwd; if add_leading_gap { preview_width = preview_width.saturating_sub(2); } let preview = truncate_text(row.display_preview(), preview_width); let mut spans: Vec = vec![marker]; + if let Some(created) = created_span { + spans.push(created); + spans.push(" ".into()); + } if let Some(updated) = updated_span { spans.push(updated); spans.push(" ".into()); @@ -1082,26 +1124,44 @@ fn format_updated_label(row: &Row) -> String { } } +fn format_created_label(row: &Row) -> String { + match row.created_at { + Some(created) => human_time_ago(created), + None => "-".to_string(), + } +} + fn render_column_headers( frame: &mut crate::custom_terminal::Frame, area: Rect, metrics: &ColumnMetrics, + sort_key: ThreadSortKey, ) { if area.height == 0 { return; } let mut spans: Vec = vec![" ".into()]; - if metrics.max_updated_width > 0 { + let visibility = column_visibility(area.width, metrics, sort_key); + if visibility.show_created { let label = format!( "{text: 0 { + if visibility.show_branch { let label = format!( "{text: 0 { + if visibility.show_cwd { let label = format!( "{text:, + /// (created_label, updated_label, branch_label, cwd_label) per row. + labels: Vec<(String, String, String, String)>, +} + +/// Determines which columns to render given available terminal width. +/// +/// When the terminal is narrow, only one timestamp column is shown (whichever +/// matches the current sort key). Branch and CWD are hidden if their max +/// widths are zero (no data to show). +#[derive(Debug, PartialEq, Eq)] +struct ColumnVisibility { + show_created: bool, + show_updated: bool, + show_branch: bool, + show_cwd: bool, } fn calculate_column_metrics(rows: &[Row], include_cwd: bool) -> ColumnMetrics { @@ -1150,8 +1229,9 @@ fn calculate_column_metrics(rows: &[Row], include_cwd: bool) -> ColumnMetrics { format!("…{tail}") } - let mut labels: Vec<(String, String, String)> = Vec::with_capacity(rows.len()); - let mut max_updated_width = UnicodeWidthStr::width("Updated"); + let mut labels: Vec<(String, String, String, String)> = Vec::with_capacity(rows.len()); + let mut max_created_width = UnicodeWidthStr::width("Created at"); + let mut max_updated_width = UnicodeWidthStr::width("Updated at"); let mut max_branch_width = UnicodeWidthStr::width("Branch"); let mut max_cwd_width = if include_cwd { UnicodeWidthStr::width("CWD") @@ -1160,6 +1240,7 @@ fn calculate_column_metrics(rows: &[Row], include_cwd: bool) -> ColumnMetrics { }; for row in rows { + let created = format_created_label(row); let updated = format_updated_label(row); let branch_raw = row.git_branch.clone().unwrap_or_default(); let branch = right_elide(&branch_raw, 24); @@ -1173,13 +1254,15 @@ fn calculate_column_metrics(rows: &[Row], include_cwd: bool) -> ColumnMetrics { } else { String::new() }; + max_created_width = max_created_width.max(UnicodeWidthStr::width(created.as_str())); max_updated_width = max_updated_width.max(UnicodeWidthStr::width(updated.as_str())); max_branch_width = max_branch_width.max(UnicodeWidthStr::width(branch.as_str())); max_cwd_width = max_cwd_width.max(UnicodeWidthStr::width(cwd.as_str())); - labels.push((updated, branch, cwd)); + labels.push((created, updated, branch, cwd)); } ColumnMetrics { + max_created_width, max_updated_width, max_branch_width, max_cwd_width, @@ -1187,37 +1270,95 @@ fn calculate_column_metrics(rows: &[Row], include_cwd: bool) -> ColumnMetrics { } } +/// Computes which columns fit in the available width. +/// +/// The algorithm reserves at least `MIN_PREVIEW_WIDTH` characters for the +/// conversation preview. If both timestamp columns don't fit, only the one +/// matching the current sort key is shown. +fn column_visibility( + area_width: u16, + metrics: &ColumnMetrics, + sort_key: ThreadSortKey, +) -> ColumnVisibility { + const MIN_PREVIEW_WIDTH: usize = 10; + + let show_branch = metrics.max_branch_width > 0; + let show_cwd = metrics.max_cwd_width > 0; + + // Calculate remaining width after all optional columns. + let mut preview_width = area_width as usize; + preview_width = preview_width.saturating_sub(2); // marker + if metrics.max_created_width > 0 { + preview_width = preview_width.saturating_sub(metrics.max_created_width + 2); + } + if metrics.max_updated_width > 0 { + preview_width = preview_width.saturating_sub(metrics.max_updated_width + 2); + } + if show_branch { + preview_width = preview_width.saturating_sub(metrics.max_branch_width + 2); + } + if show_cwd { + preview_width = preview_width.saturating_sub(metrics.max_cwd_width + 2); + } + + // If preview would be too narrow, hide the non-active timestamp column. + let show_both = preview_width >= MIN_PREVIEW_WIDTH; + let show_created = if show_both { + metrics.max_created_width > 0 + } else { + sort_key == ThreadSortKey::CreatedAt + }; + let show_updated = if show_both { + metrics.max_updated_width > 0 + } else { + sort_key == ThreadSortKey::UpdatedAt + }; + + ColumnVisibility { + show_created, + show_updated, + show_branch, + show_cwd, + } +} + #[cfg(test)] mod tests { use super::*; use chrono::Duration; + use codex_protocol::ThreadId; + use codex_protocol::protocol::EventMsg; + use codex_protocol::protocol::RolloutItem; + use codex_protocol::protocol::RolloutLine; + use codex_protocol::protocol::SessionMeta; + use codex_protocol::protocol::SessionMetaLine; + use codex_protocol::protocol::SessionSource; + use codex_protocol::protocol::UserMessageEvent; use crossterm::event::KeyCode; use crossterm::event::KeyEvent; use crossterm::event::KeyModifiers; use insta::assert_snapshot; + use pretty_assertions::assert_eq; use serde_json::json; + use std::fs::FileTimes; + use std::fs::OpenOptions; + use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use std::sync::Mutex; - fn head_with_ts_and_user_text(ts: &str, texts: &[&str]) -> Vec { - vec![ - json!({ "timestamp": ts }), - json!({ - "type": "message", - "role": "user", - "content": texts - .iter() - .map(|t| json!({ "type": "input_text", "text": *t })) - .collect::>() - }), - ] - } - fn make_item(path: &str, ts: &str, preview: &str) -> ThreadItem { ThreadItem { path: PathBuf::from(path), - head: head_with_ts_and_user_text(ts, &[preview]), + thread_id: None, + first_user_message: Some(preview.to_string()), + cwd: None, + git_branch: None, + git_sha: None, + git_origin_url: None, + source: None, + model_provider: None, + cli_version: None, created_at: Some(ts.to_string()), updated_at: Some(ts.to_string()), } @@ -1242,40 +1383,120 @@ mod tests { } } + fn set_rollout_mtime(path: &Path, updated_at: DateTime) { + let times = FileTimes::new().set_modified(updated_at.into()); + OpenOptions::new() + .append(true) + .open(path) + .expect("open rollout") + .set_times(times) + .expect("set times"); + } + + #[tokio::test] + async fn resume_picker_orders_by_updated_at() { + use uuid::Uuid; + + let tempdir = tempfile::tempdir().expect("tempdir"); + let sessions_root = tempdir.path().join("sessions"); + std::fs::create_dir_all(&sessions_root).expect("mkdir sessions root"); + + let now = Utc::now(); + + let write_rollout = |ts: DateTime, preview: &str| -> PathBuf { + let dir = sessions_root + .join(ts.format("%Y").to_string()) + .join(ts.format("%m").to_string()) + .join(ts.format("%d").to_string()); + std::fs::create_dir_all(&dir).expect("mkdir date dirs"); + let filename = format!( + "rollout-{}-{}.jsonl", + ts.format("%Y-%m-%dT%H-%M-%S"), + Uuid::new_v4() + ); + let path = dir.join(filename); + let meta = SessionMeta { + id: ThreadId::new(), + forked_from_id: None, + timestamp: ts.to_rfc3339(), + cwd: PathBuf::from("/tmp"), + originator: String::from("user"), + cli_version: String::from("0.0.0"), + source: SessionSource::Cli, + model_provider: Some(String::from("openai")), + base_instructions: None, + dynamic_tools: None, + }; + let meta_line = RolloutLine { + timestamp: ts.to_rfc3339(), + item: RolloutItem::SessionMeta(SessionMetaLine { meta, git: None }), + }; + let user_line = RolloutLine { + timestamp: ts.to_rfc3339(), + item: RolloutItem::EventMsg(EventMsg::UserMessage(UserMessageEvent { + message: preview.to_string(), + images: None, + text_elements: Vec::new(), + local_images: Vec::new(), + })), + }; + let meta_json = serde_json::to_string(&meta_line).expect("serialize meta"); + let user_json = serde_json::to_string(&user_line).expect("serialize user"); + std::fs::write(&path, format!("{meta_json}\n{user_json}\n")).expect("write rollout"); + path + }; + + let created_a = now - Duration::minutes(1); + let created_b = now - Duration::minutes(2); + + let path_a = write_rollout(created_a, "A (created newer)"); + let path_b = write_rollout(created_b, "B (created older)"); + + set_rollout_mtime(&path_a, now - Duration::minutes(10)); + set_rollout_mtime(&path_b, now - Duration::seconds(10)); + + let page = RolloutRecorder::list_threads( + tempdir.path(), + PAGE_SIZE, + None, + ThreadSortKey::UpdatedAt, + INTERACTIVE_SESSION_SOURCES, + Some(&[String::from("openai")]), + "openai", + ) + .await + .expect("list threads"); + + let rows = rows_from_items(page.items); + let previews: Vec = rows.iter().map(|row| row.preview.clone()).collect(); + + assert_eq!( + previews, + vec![ + "B (created older)".to_string(), + "A (created newer)".to_string() + ] + ); + } + #[test] - fn preview_uses_first_message_input_text() { - let head = vec![ - json!({ "timestamp": "2025-01-01T00:00:00Z" }), - json!({ - "type": "message", - "role": "user", - "content": [ - { "type": "input_text", "text": "# AGENTS.md instructions for project\n\n\nhi\n" }, - ] - }), - json!({ - "type": "message", - "role": "user", - "content": [ - { "type": "input_text", "text": "..." }, - ] - }), - json!({ - "type": "message", - "role": "user", - "content": [ - { "type": "input_text", "text": "real question" }, - { "type": "input_image", "image_url": "ignored" } - ] - }), - json!({ - "type": "message", - "role": "user", - "content": [ { "type": "input_text", "text": "later text" } ] - }), - ]; - let preview = preview_from_head(&head); - assert_eq!(preview.as_deref(), Some("real question")); + fn head_to_row_uses_first_user_message() { + let item = ThreadItem { + path: PathBuf::from("/tmp/a.jsonl"), + thread_id: None, + first_user_message: Some("real question".to_string()), + cwd: None, + git_branch: None, + git_sha: None, + git_origin_url: None, + source: None, + model_provider: None, + cli_version: None, + created_at: Some("2025-01-01T00:00:00Z".into()), + updated_at: Some("2025-01-01T00:00:00Z".into()), + }; + let row = head_to_row(&item); + assert_eq!(row.preview, "real question"); } #[test] @@ -1283,13 +1504,29 @@ mod tests { // Construct two items with different timestamps and real user text. let a = ThreadItem { path: PathBuf::from("/tmp/a.jsonl"), - head: head_with_ts_and_user_text("2025-01-01T00:00:00Z", &["A"]), + thread_id: None, + first_user_message: Some("A".to_string()), + cwd: None, + git_branch: None, + git_sha: None, + git_origin_url: None, + source: None, + model_provider: None, + cli_version: None, created_at: Some("2025-01-01T00:00:00Z".into()), updated_at: Some("2025-01-01T00:00:00Z".into()), }; let b = ThreadItem { path: PathBuf::from("/tmp/b.jsonl"), - head: head_with_ts_and_user_text("2025-01-02T00:00:00Z", &["B"]), + thread_id: None, + first_user_message: Some("B".to_string()), + cwd: None, + git_branch: None, + git_sha: None, + git_origin_url: None, + source: None, + model_provider: None, + cli_version: None, created_at: Some("2025-01-02T00:00:00Z".into()), updated_at: Some("2025-01-02T00:00:00Z".into()), }; @@ -1302,10 +1539,17 @@ mod tests { #[test] fn row_uses_tail_timestamp_for_updated_at() { - let head = head_with_ts_and_user_text("2025-01-01T00:00:00Z", &["Hello"]); let item = ThreadItem { path: PathBuf::from("/tmp/a.jsonl"), - head, + thread_id: None, + first_user_message: Some("Hello".to_string()), + cwd: None, + git_branch: None, + git_sha: None, + git_origin_url: None, + source: None, + model_provider: None, + cli_version: None, created_at: Some("2025-01-01T00:00:00Z".into()), updated_at: Some("2025-01-01T01:00:00Z".into()), }; @@ -1409,7 +1653,7 @@ mod tests { let area = frame.area(); let segments = Layout::vertical([Constraint::Length(1), Constraint::Min(1)]).split(area); - render_column_headers(&mut frame, segments[0], &metrics); + render_column_headers(&mut frame, segments[0], &metrics, state.sort_key); render_list(&mut frame, segments[1], &state, &metrics); } terminal.flush().expect("flush"); @@ -1552,13 +1796,19 @@ mod tests { .areas(area); frame.render_widget_ref( - Line::from(vec!["Resume a previous session".bold().cyan()]), + Line::from(vec![ + "Resume a previous session".bold().cyan(), + " ".into(), + "Sort:".dim(), + " ".into(), + "Created at".magenta(), + ]), header, ); frame.render_widget_ref(Line::from("Type to search".dim()), search); - render_column_headers(&mut frame, columns, &metrics); + render_column_headers(&mut frame, columns, &metrics, state.sort_key); render_list(&mut frame, list, &state, &metrics); let hint_line: Line = vec![ @@ -1570,6 +1820,9 @@ mod tests { " ".dim(), key_hint::ctrl(KeyCode::Char('c')).into(), " to quit ".dim(), + " ".dim(), + key_hint::plain(KeyCode::Tab).into(), + " to toggle sort ".dim(), ] .into(); frame.render_widget_ref(hint_line, hint); @@ -1669,7 +1922,7 @@ mod tests { let area = frame.area(); let segments = Layout::vertical([Constraint::Length(1), Constraint::Min(1)]).split(area); - render_column_headers(&mut frame, segments[0], &metrics); + render_column_headers(&mut frame, segments[0], &metrics, state.sort_key); render_list(&mut frame, segments[1], &state, &metrics); } terminal.flush().expect("flush"); @@ -1779,6 +2032,85 @@ mod tests { assert!(guard[0].search_token.is_none()); } + #[test] + fn column_visibility_hides_extra_date_column_when_narrow() { + let metrics = ColumnMetrics { + max_created_width: 8, + max_updated_width: 12, + max_branch_width: 0, + max_cwd_width: 0, + labels: Vec::new(), + }; + + let created = column_visibility(30, &metrics, ThreadSortKey::CreatedAt); + assert_eq!( + created, + ColumnVisibility { + show_created: true, + show_updated: false, + show_branch: false, + show_cwd: false, + } + ); + + let updated = column_visibility(30, &metrics, ThreadSortKey::UpdatedAt); + assert_eq!( + updated, + ColumnVisibility { + show_created: false, + show_updated: true, + show_branch: false, + show_cwd: false, + } + ); + + let wide = column_visibility(40, &metrics, ThreadSortKey::CreatedAt); + assert_eq!( + wide, + ColumnVisibility { + show_created: true, + show_updated: true, + show_branch: false, + show_cwd: false, + } + ); + } + + #[tokio::test] + async fn toggle_sort_key_reloads_with_new_sort() { + let recorded_requests: Arc>> = Arc::new(Mutex::new(Vec::new())); + let request_sink = recorded_requests.clone(); + let loader: PageLoader = Arc::new(move |req: PageLoadRequest| { + request_sink.lock().unwrap().push(req); + }); + + let mut state = PickerState::new( + PathBuf::from("/tmp"), + FrameRequester::test_dummy(), + loader, + String::from("openai"), + true, + None, + SessionPickerAction::Resume, + ); + + state.start_initial_load(); + { + let guard = recorded_requests.lock().unwrap(); + assert_eq!(guard.len(), 1); + assert_eq!(guard[0].sort_key, ThreadSortKey::CreatedAt); + } + + state + .handle_key(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE)) + .await + .unwrap(); + + let guard = recorded_requests.lock().unwrap(); + assert_eq!(guard.len(), 2); + assert_eq!(guard[1].sort_key, ThreadSortKey::UpdatedAt); + } + #[tokio::test] async fn page_navigation_uses_view_rows() { let loader: PageLoader = Arc::new(|_| {}); diff --git a/codex-rs/tui/src/slash_command.rs b/codex-rs/tui/src/slash_command.rs index a71b1ddbe5..6bb41b4696 100644 --- a/codex-rs/tui/src/slash_command.rs +++ b/codex-rs/tui/src/slash_command.rs @@ -33,6 +33,8 @@ pub enum SlashCommand { Diff, Mention, Status, + DebugConfig, + Statusline, Mcp, Apps, Logout, @@ -63,6 +65,8 @@ impl SlashCommand { SlashCommand::Mention => "mention a file", SlashCommand::Skills => "use skills to improve how Codex performs specific tasks", SlashCommand::Status => "show current session configuration and token usage", + SlashCommand::DebugConfig => "show config layers and requirement sources for debugging", + SlashCommand::Statusline => "configure which items appear in the status line", SlashCommand::Ps => "list background terminals", SlashCommand::Model => "choose what model and reasoning effort to use", SlashCommand::Personality => "choose a communication style for Codex", @@ -118,6 +122,7 @@ impl SlashCommand { | SlashCommand::Mention | SlashCommand::Skills | SlashCommand::Status + | SlashCommand::DebugConfig | SlashCommand::Ps | SlashCommand::Mcp | SlashCommand::Apps @@ -128,6 +133,7 @@ impl SlashCommand { SlashCommand::TestApproval => true, SlashCommand::Collab => true, SlashCommand::Agent => true, + SlashCommand::Statusline => false, } } diff --git a/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_screen.snap b/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_screen.snap index 79a169a06d..885ba88842 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_screen.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_screen.snap @@ -1,14 +1,13 @@ --- source: tui/src/resume_picker.rs -assertion_line: 1438 expression: snapshot --- -Resume a previous session +Resume a previous session Sort: Created at Type to search - Updated Branch CWD Conversation + Created at Updated at Branch CWD Conversation No sessions yet -enter to resume esc to start new ctrl + c to quit +enter to resume esc to start new ctrl + c to quit tab to toggle sort diff --git a/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_table.snap b/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_table.snap index c029043219..1505d6e7e3 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_table.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_table.snap @@ -2,7 +2,7 @@ source: tui/src/resume_picker.rs expression: snapshot --- - Updated Branch CWD Conversation - 42 seconds ago - - Fix resume picker timestamps -> 35 minutes ago - - Investigate lazy pagination cap - 2 hours ago - - Explain the codebase + Created at Updated at Branch CWD Conversation + 16 minutes ago 42 seconds ago - - Fix resume picker timestamps +> 1 hour ago 35 minutes ago - - Investigate lazy pagination cap + 2 hours ago 2 hours ago - - Explain the codebase diff --git a/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_thread_names.snap b/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_thread_names.snap index e9bca47d40..d001fff0b9 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_thread_names.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__resume_picker__tests__resume_picker_thread_names.snap @@ -1,8 +1,7 @@ --- source: tui/src/resume_picker.rs -assertion_line: 1683 expression: snapshot --- - Updated Branch CWD Conversation -> 2 days ago - - Keep this for now - 3 days ago - - Named thread + Created at Updated at Branch CWD Conversation +> - 2 days ago - - Keep this for now + - 3 days ago - - Named thread diff --git a/codex-rs/tui/src/status/helpers.rs b/codex-rs/tui/src/status/helpers.rs index bc4cbb9c16..c819405412 100644 --- a/codex-rs/tui/src/status/helpers.rs +++ b/codex-rs/tui/src/status/helpers.rs @@ -3,7 +3,7 @@ use crate::text_formatting; use chrono::DateTime; use chrono::Local; use codex_core::AuthManager; -use codex_core::CodexAuth; +use codex_core::auth::AuthMode as CoreAuthMode; use codex_core::config::Config; use codex_core::project_doc::discover_project_doc_paths; use codex_protocol::account::PlanType; @@ -90,15 +90,15 @@ pub(crate) fn compose_account_display( ) -> Option { let auth = auth_manager.auth_cached()?; - match auth { - CodexAuth::Chatgpt(_) | CodexAuth::ChatgptAuthTokens(_) => { + match auth.auth_mode() { + CoreAuthMode::ApiKey => Some(StatusAccountDisplay::ApiKey), + CoreAuthMode::Chatgpt => { let email = auth.get_account_email(); let plan = plan .map(|plan_type| title_case(format!("{plan_type:?}").as_str())) .or_else(|| Some("Unknown".to_string())); Some(StatusAccountDisplay::ChatGpt { email, plan }) } - CodexAuth::ApiKey(_) => Some(StatusAccountDisplay::ApiKey), } } diff --git a/codex-rs/tui/src/status/mod.rs b/codex-rs/tui/src/status/mod.rs index 89c8daefdc..4f46af1cbc 100644 --- a/codex-rs/tui/src/status/mod.rs +++ b/codex-rs/tui/src/status/mod.rs @@ -1,3 +1,11 @@ +//! Status output formatting and display adapters for the TUI. +//! +//! This module turns protocol-level snapshots into stable display structures used by `/status` +//! output and footer/status-line helpers, while keeping rendering concerns out of transport-facing +//! code. +//! +//! `rate_limits` is the main integration point for status-line usage-limit items: it converts raw +//! window snapshots into local-time labels and classifies data as available, stale, or missing. mod account; mod card; mod format; @@ -5,8 +13,10 @@ mod helpers; mod rate_limits; pub(crate) use card::new_status_output; +pub(crate) use helpers::format_directory_display; pub(crate) use helpers::format_tokens_compact; pub(crate) use rate_limits::RateLimitSnapshotDisplay; +pub(crate) use rate_limits::RateLimitWindowDisplay; pub(crate) use rate_limits::rate_limit_snapshot_display; #[cfg(test)] diff --git a/codex-rs/tui/src/status/rate_limits.rs b/codex-rs/tui/src/status/rate_limits.rs index 3fae3ac295..830fe158b2 100644 --- a/codex-rs/tui/src/status/rate_limits.rs +++ b/codex-rs/tui/src/status/rate_limits.rs @@ -1,3 +1,10 @@ +//! Rate-limit and credits display shaping for status surfaces. +//! +//! This module maps `RateLimitSnapshot` protocol payloads into display-oriented rows that the TUI +//! can render in `/status` and status-line contexts without duplicating formatting logic. +//! +//! The key contract is that time-sensitive values are interpreted relative to a caller-provided +//! capture timestamp so stale detection and reset labels remain coherent for a given draw cycle. use crate::chatwidget::get_limits_duration; use crate::text_formatting::capitalize_first; @@ -16,42 +23,58 @@ const STATUS_LIMIT_BAR_EMPTY: &str = "░"; #[derive(Debug, Clone)] pub(crate) struct StatusRateLimitRow { + /// Human-readable row label, such as `"5h limit"` or `"Credits"`. pub label: String, + /// Value payload for the row. pub value: StatusRateLimitValue, } +/// Display value variants for a single rate-limit row. #[derive(Debug, Clone)] pub(crate) enum StatusRateLimitValue { + /// Percent-based usage window with optional reset timestamp text. Window { + /// Percent of the window that has been consumed. percent_used: f64, + /// Localized reset string, or `None` when unknown. resets_at: Option, }, + /// Plain text value used for non-window rows. Text(String), } +/// Availability state for rate-limit data shown in status output. #[derive(Debug, Clone)] pub(crate) enum StatusRateLimitData { + /// Snapshot data is recent enough for normal rendering. Available(Vec), + /// Snapshot data exists but is older than the staleness threshold. Stale(Vec), + /// No snapshot data is currently available. Missing, } +/// Maximum age before a snapshot is considered stale in status output. pub(crate) const RATE_LIMIT_STALE_THRESHOLD_MINUTES: i64 = 15; +/// Display-friendly representation of one usage window from a snapshot. #[derive(Debug, Clone)] pub(crate) struct RateLimitWindowDisplay { + /// Percent used for the window. pub used_percent: f64, + /// Human-readable local reset time. pub resets_at: Option, + /// Window length in minutes when provided by the server. pub window_minutes: Option, } impl RateLimitWindowDisplay { fn from_window(window: &RateLimitWindow, captured_at: DateTime) -> Self { - let resets_at = window + let resets_at_utc = window .resets_at .and_then(|seconds| DateTime::::from_timestamp(seconds, 0)) - .map(|dt| dt.with_timezone(&Local)) - .map(|dt| format_reset_timestamp(dt, captured_at)); + .map(|dt| dt.with_timezone(&Local)); + let resets_at = resets_at_utc.map(|dt| format_reset_timestamp(dt, captured_at)); Self { used_percent: window.used_percent, @@ -63,19 +86,31 @@ impl RateLimitWindowDisplay { #[derive(Debug, Clone)] pub(crate) struct RateLimitSnapshotDisplay { + /// Local timestamp representing when this display snapshot was captured. pub captured_at: DateTime, + /// Primary usage window (typically short duration). pub primary: Option, + /// Secondary usage window (typically weekly). pub secondary: Option, + /// Optional credits metadata when available. pub credits: Option, } +/// Display-ready credits state extracted from protocol snapshots. #[derive(Debug, Clone)] pub(crate) struct CreditsSnapshotDisplay { + /// Whether credits tracking is enabled for the account. pub has_credits: bool, + /// Whether the account has unlimited credits. pub unlimited: bool, + /// Raw balance text as provided by the backend. pub balance: Option, } +/// Converts a protocol snapshot into UI-friendly display data. +/// +/// Pass the timestamp from the same observation point as `snapshot`; supplying a significantly +/// older or newer `captured_at` can produce misleading reset labels and stale classification. pub(crate) fn rate_limit_snapshot_display( snapshot: &RateLimitSnapshot, captured_at: DateTime, @@ -104,6 +139,10 @@ impl From<&CoreCreditsSnapshot> for CreditsSnapshotDisplay { } } +/// Builds display rows from a snapshot and marks stale data by capture age. +/// +/// Callers should pass `Local::now()` for `now` at render time; using a cached timestamp can make +/// fresh data appear stale or prevent stale warnings from appearing. pub(crate) fn compose_rate_limit_data( snapshot: Option<&RateLimitSnapshotDisplay>, now: DateTime, @@ -163,6 +202,10 @@ pub(crate) fn compose_rate_limit_data( } } +/// Renders a fixed-width progress bar from remaining percentage. +/// +/// This function expects a remaining value in the `0..=100` range and clamps out-of-range input. +/// Passing a used percentage by mistake will invert the bar and mislead users. pub(crate) fn render_status_limit_progress_bar(percent_remaining: f64) -> String { let ratio = (percent_remaining / 100.0).clamp(0.0, 1.0); let filled = (ratio * STATUS_LIMIT_BAR_SEGMENTS as f64).round() as usize; @@ -175,6 +218,7 @@ pub(crate) fn render_status_limit_progress_bar(percent_remaining: f64) -> String ) } +/// Formats a compact textual summary from remaining percentage. pub(crate) fn format_status_limit_summary(percent_remaining: f64) -> String { format!("{percent_remaining:.0}% left") } diff --git a/codex-rs/tui/src/streaming/chunking.rs b/codex-rs/tui/src/streaming/chunking.rs new file mode 100644 index 0000000000..b653f51ecb --- /dev/null +++ b/codex-rs/tui/src/streaming/chunking.rs @@ -0,0 +1,439 @@ +//! Adaptive stream chunking policy for commit animation ticks. +//! +//! This policy preserves the baseline user experience while adapting to bursty +//! stream input. In [`ChunkingMode::Smooth`], one queued line is drained per +//! baseline commit tick. When queue pressure rises, it switches to +//! [`ChunkingMode::CatchUp`] and drains queued backlog immediately so display +//! lag converges as quickly as possible. +//! +//! The policy is source-agnostic: it depends only on queue depth and queue +//! age from [`QueueSnapshot`]. It does not branch on source identity or explicit +//! throughput targets. +//! +//! # Mental model +//! +//! Think of this as a two-gear system: +//! +//! - [`ChunkingMode::Smooth`]: steady baseline display pacing. +//! - [`ChunkingMode::CatchUp`]: full queue draining while backlog exists. +//! +//! The transition logic intentionally uses hysteresis: +//! +//! - enter catch-up on higher-pressure thresholds +//! - exit catch-up on lower-pressure thresholds, held for [`EXIT_HOLD`] +//! - after exit, suppress immediate re-entry for [`REENTER_CATCH_UP_HOLD`] +//! unless backlog is severe +//! +//! This avoids rapid gear-flapping near threshold boundaries. +//! +//! # Policy flow +//! +//! On each decision tick, [`AdaptiveChunkingPolicy::decide`] does: +//! +//! 1. If queue is empty, reset to [`ChunkingMode::Smooth`]. +//! 2. If currently smooth, call [`AdaptiveChunkingPolicy::maybe_enter_catch_up`]. +//! 3. If currently catch-up, call [`AdaptiveChunkingPolicy::maybe_exit_catch_up`]. +//! 4. Build [`DrainPlan`] (`Single` for smooth, `Batch(queued_lines)` for catch-up). +//! +//! # Concrete examples +//! +//! With current defaults: +//! +//! - `Smooth` drains one line per commit tick. +//! - `CatchUp` drains all currently queued lines in a tick. +//! +//! # Tuning guide (in code terms) +//! +//! Prefer tuning in this order so causes remain clear: +//! +//! 1. enter/exit thresholds: [`ENTER_QUEUE_DEPTH_LINES`], [`ENTER_OLDEST_AGE`], +//! [`EXIT_QUEUE_DEPTH_LINES`], [`EXIT_OLDEST_AGE`] +//! 2. hysteresis windows: [`EXIT_HOLD`], [`REENTER_CATCH_UP_HOLD`] +//! 3. severe gates: [`SEVERE_QUEUE_DEPTH_LINES`], [`SEVERE_OLDEST_AGE`] +//! +//! Symptom-oriented adjustments: +//! +//! - lag starts too late: lower enter thresholds +//! - frequent smooth/catch-up chatter: increase hold windows, or tighten exit +//! thresholds +//! - catch-up re-enters too eagerly after exit: increase re-entry hold +//! +//! # Responsibilities +//! +//! - track mode and hysteresis state +//! - produce deterministic [`ChunkingDecision`] values from queue snapshots +//! - preserve queue order by draining from queue head only +//! +//! # Non-responsibilities +//! +//! - scheduling commit ticks +//! - reordering stream lines +//! - transport/source-specific semantics +//! +//! Markdown docs remain supplemental: +//! +//! - `docs/tui-stream-chunking-review.md` +//! - `docs/tui-stream-chunking-tuning.md` +//! - `docs/tui-stream-chunking-validation.md` + +use std::time::Duration; +use std::time::Instant; + +/// Queue-depth threshold that allows entering catch-up mode. +/// +/// Crossing this threshold alone is sufficient to leave smooth mode. +const ENTER_QUEUE_DEPTH_LINES: usize = 8; + +/// Oldest-line age threshold that allows entering catch-up mode. +/// +/// Crossing this threshold alone is sufficient to leave smooth mode. +const ENTER_OLDEST_AGE: Duration = Duration::from_millis(120); + +/// Queue-depth threshold used when evaluating catch-up exit hysteresis. +/// +/// Depth must be at or below this value before exit hold timing can begin. +const EXIT_QUEUE_DEPTH_LINES: usize = 2; + +/// Oldest-line age threshold used when evaluating catch-up exit hysteresis. +/// +/// Age must be at or below this value before exit hold timing can begin. +const EXIT_OLDEST_AGE: Duration = Duration::from_millis(40); + +/// Minimum duration queue pressure must stay below exit thresholds to leave catch-up mode. +const EXIT_HOLD: Duration = Duration::from_millis(250); + +/// Cooldown window after a catch-up exit that suppresses immediate re-entry. +/// +/// Severe backlog still bypasses this hold to avoid unbounded queue-age growth. +const REENTER_CATCH_UP_HOLD: Duration = Duration::from_millis(250); + +/// Queue-depth cutoff that marks backlog as severe for faster convergence. +/// +/// This threshold is used to bypass re-entry hold after a recent catch-up exit. +const SEVERE_QUEUE_DEPTH_LINES: usize = 64; + +/// Oldest-line age cutoff that marks backlog as severe for faster convergence. +const SEVERE_OLDEST_AGE: Duration = Duration::from_millis(300); + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub(crate) enum ChunkingMode { + /// Drain one line per baseline commit tick. + #[default] + Smooth, + /// Drain multiple lines per tick according to queue pressure. + CatchUp, +} + +/// Captures queue pressure inputs used by adaptive chunking decisions. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub(crate) struct QueueSnapshot { + /// Number of queued stream lines waiting to be displayed. + pub(crate) queued_lines: usize, + /// Age of the oldest queued line at decision time. + pub(crate) oldest_age: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum DrainPlan { + /// Emit exactly one queued line. + Single, + /// Emit up to `usize` queued lines. + Batch(usize), +} + +/// Represents one policy decision for a specific queue snapshot. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct ChunkingDecision { + /// Mode after applying hysteresis transitions for this decision. + pub(crate) mode: ChunkingMode, + /// Whether this decision transitioned from `Smooth` into `CatchUp`. + pub(crate) entered_catch_up: bool, + /// Drain plan to execute for the current commit tick. + pub(crate) drain_plan: DrainPlan, +} + +/// Maintains adaptive chunking mode and hysteresis state across ticks. +#[derive(Debug, Default)] +pub(crate) struct AdaptiveChunkingPolicy { + mode: ChunkingMode, + below_exit_threshold_since: Option, + last_catch_up_exit_at: Option, +} + +impl AdaptiveChunkingPolicy { + /// Returns the policy mode used by the most recent decision. + pub(crate) fn mode(&self) -> ChunkingMode { + self.mode + } + + /// Resets state to baseline smooth mode. + pub(crate) fn reset(&mut self) { + self.mode = ChunkingMode::Smooth; + self.below_exit_threshold_since = None; + self.last_catch_up_exit_at = None; + } + + /// Computes a drain decision from the current queue snapshot. + /// + /// The decision is deterministic for a given `(mode, snapshot, now)` triple. Callers should + /// avoid inventing synthetic snapshots; stale queue age data can cause premature catch-up exits. + pub(crate) fn decide(&mut self, snapshot: QueueSnapshot, now: Instant) -> ChunkingDecision { + if snapshot.queued_lines == 0 { + self.note_catch_up_exit(now); + self.mode = ChunkingMode::Smooth; + self.below_exit_threshold_since = None; + return ChunkingDecision { + mode: self.mode, + entered_catch_up: false, + drain_plan: DrainPlan::Single, + }; + } + + let entered_catch_up = match self.mode { + ChunkingMode::Smooth => self.maybe_enter_catch_up(snapshot, now), + ChunkingMode::CatchUp => { + self.maybe_exit_catch_up(snapshot, now); + false + } + }; + + let drain_plan = match self.mode { + ChunkingMode::Smooth => DrainPlan::Single, + ChunkingMode::CatchUp => DrainPlan::Batch(snapshot.queued_lines.max(1)), + }; + + ChunkingDecision { + mode: self.mode, + entered_catch_up, + drain_plan, + } + } + + /// Switches from `Smooth` to `CatchUp` when enter thresholds are crossed. + /// + /// Returns `true` only on the transition tick so callers can emit one-shot + /// transition observability. + fn maybe_enter_catch_up(&mut self, snapshot: QueueSnapshot, now: Instant) -> bool { + if !should_enter_catch_up(snapshot) { + return false; + } + if self.reentry_hold_active(now) && !is_severe_backlog(snapshot) { + return false; + } + self.mode = ChunkingMode::CatchUp; + self.below_exit_threshold_since = None; + self.last_catch_up_exit_at = None; + true + } + + /// Applies exit hysteresis while in `CatchUp` mode. + /// + /// The policy requires queue pressure to stay below exit thresholds for the + /// full `EXIT_HOLD` window before returning to `Smooth`. + fn maybe_exit_catch_up(&mut self, snapshot: QueueSnapshot, now: Instant) { + if !should_exit_catch_up(snapshot) { + self.below_exit_threshold_since = None; + return; + } + + match self.below_exit_threshold_since { + Some(since) if now.saturating_duration_since(since) >= EXIT_HOLD => { + self.mode = ChunkingMode::Smooth; + self.below_exit_threshold_since = None; + self.last_catch_up_exit_at = Some(now); + } + Some(_) => {} + None => { + self.below_exit_threshold_since = Some(now); + } + } + } + + fn note_catch_up_exit(&mut self, now: Instant) { + if self.mode == ChunkingMode::CatchUp { + self.last_catch_up_exit_at = Some(now); + } + } + + fn reentry_hold_active(&self, now: Instant) -> bool { + self.last_catch_up_exit_at + .is_some_and(|exit| now.saturating_duration_since(exit) < REENTER_CATCH_UP_HOLD) + } +} + +/// Returns whether current queue pressure warrants entering catch-up mode. +/// +/// Either depth or age pressure is sufficient to trigger catch-up. +fn should_enter_catch_up(snapshot: QueueSnapshot) -> bool { + snapshot.queued_lines >= ENTER_QUEUE_DEPTH_LINES + || snapshot + .oldest_age + .is_some_and(|oldest| oldest >= ENTER_OLDEST_AGE) +} + +/// Returns whether queue pressure is low enough to begin exit hysteresis. +/// +/// Both depth and age must be below thresholds; this prevents oscillation when +/// one signal is still under load. +fn should_exit_catch_up(snapshot: QueueSnapshot) -> bool { + snapshot.queued_lines <= EXIT_QUEUE_DEPTH_LINES + && snapshot + .oldest_age + .is_some_and(|oldest| oldest <= EXIT_OLDEST_AGE) +} + +/// Returns whether backlog is severe enough to use a faster catch-up target. +/// +/// Severe pressure bypasses re-entry hold to avoid queue-age growth after a +/// recent catch-up exit. +fn is_severe_backlog(snapshot: QueueSnapshot) -> bool { + snapshot.queued_lines >= SEVERE_QUEUE_DEPTH_LINES + || snapshot + .oldest_age + .is_some_and(|oldest| oldest >= SEVERE_OLDEST_AGE) +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + fn snapshot(queued_lines: usize, oldest_age_ms: u64) -> QueueSnapshot { + QueueSnapshot { + queued_lines, + oldest_age: Some(Duration::from_millis(oldest_age_ms)), + } + } + + #[test] + fn smooth_mode_is_default() { + let mut policy = AdaptiveChunkingPolicy::default(); + let now = Instant::now(); + + let decision = policy.decide(snapshot(1, 10), now); + assert_eq!(decision.mode, ChunkingMode::Smooth); + assert_eq!(decision.entered_catch_up, false); + assert_eq!(decision.drain_plan, DrainPlan::Single); + } + + #[test] + fn enters_catch_up_on_depth_threshold() { + let mut policy = AdaptiveChunkingPolicy::default(); + let now = Instant::now(); + + let decision = policy.decide(snapshot(8, 10), now); + assert_eq!(decision.mode, ChunkingMode::CatchUp); + assert_eq!(decision.entered_catch_up, true); + assert_eq!(decision.drain_plan, DrainPlan::Batch(8)); + } + + #[test] + fn enters_catch_up_on_age_threshold() { + let mut policy = AdaptiveChunkingPolicy::default(); + let now = Instant::now(); + + let decision = policy.decide(snapshot(2, 120), now); + assert_eq!(decision.mode, ChunkingMode::CatchUp); + assert_eq!(decision.entered_catch_up, true); + assert_eq!(decision.drain_plan, DrainPlan::Batch(2)); + } + + #[test] + fn severe_backlog_uses_faster_paced_batches() { + let mut policy = AdaptiveChunkingPolicy::default(); + let now = Instant::now(); + let _ = policy.decide(snapshot(9, 10), now); + + let decision = policy.decide(snapshot(64, 10), now + Duration::from_millis(5)); + assert_eq!(decision.mode, ChunkingMode::CatchUp); + assert_eq!(decision.drain_plan, DrainPlan::Batch(64)); + } + + #[test] + fn catch_up_batch_drains_current_backlog() { + let mut policy = AdaptiveChunkingPolicy::default(); + let now = Instant::now(); + let decision = policy.decide(snapshot(512, 400), now); + assert_eq!(decision.mode, ChunkingMode::CatchUp); + assert_eq!(decision.drain_plan, DrainPlan::Batch(512)); + } + + #[test] + fn exits_catch_up_after_hysteresis_hold() { + let mut policy = AdaptiveChunkingPolicy::default(); + let t0 = Instant::now(); + + let _ = policy.decide(snapshot(9, 10), t0); + assert_eq!(policy.mode(), ChunkingMode::CatchUp); + + let pre_hold = policy.decide(snapshot(2, 40), t0 + Duration::from_millis(200)); + assert_eq!(pre_hold.mode, ChunkingMode::CatchUp); + + let post_hold = policy.decide(snapshot(2, 40), t0 + Duration::from_millis(460)); + assert_eq!(post_hold.mode, ChunkingMode::Smooth); + assert_eq!(post_hold.drain_plan, DrainPlan::Single); + } + + #[test] + fn drops_back_to_smooth_when_idle() { + let mut policy = AdaptiveChunkingPolicy::default(); + let now = Instant::now(); + let _ = policy.decide(snapshot(9, 10), now); + assert_eq!(policy.mode(), ChunkingMode::CatchUp); + + let decision = policy.decide( + QueueSnapshot { + queued_lines: 0, + oldest_age: None, + }, + now + Duration::from_millis(20), + ); + assert_eq!(decision.mode, ChunkingMode::Smooth); + assert_eq!(decision.drain_plan, DrainPlan::Single); + } + + #[test] + fn holds_reentry_after_catch_up_exit() { + let mut policy = AdaptiveChunkingPolicy::default(); + let t0 = Instant::now(); + + let entered = policy.decide(snapshot(8, 20), t0); + assert_eq!(entered.mode, ChunkingMode::CatchUp); + + let drained = policy.decide( + QueueSnapshot { + queued_lines: 0, + oldest_age: None, + }, + t0 + Duration::from_millis(20), + ); + assert_eq!(drained.mode, ChunkingMode::Smooth); + + let held = policy.decide(snapshot(8, 20), t0 + Duration::from_millis(120)); + assert_eq!(held.mode, ChunkingMode::Smooth); + assert_eq!(held.drain_plan, DrainPlan::Single); + + let reentered = policy.decide(snapshot(8, 20), t0 + Duration::from_millis(320)); + assert_eq!(reentered.mode, ChunkingMode::CatchUp); + assert_eq!(reentered.drain_plan, DrainPlan::Batch(8)); + } + + #[test] + fn severe_backlog_can_reenter_during_hold() { + let mut policy = AdaptiveChunkingPolicy::default(); + let t0 = Instant::now(); + + let _ = policy.decide(snapshot(8, 20), t0); + let _ = policy.decide( + QueueSnapshot { + queued_lines: 0, + oldest_age: None, + }, + t0 + Duration::from_millis(20), + ); + + let severe = policy.decide(snapshot(64, 20), t0 + Duration::from_millis(120)); + assert_eq!(severe.mode, ChunkingMode::CatchUp); + assert_eq!(severe.drain_plan, DrainPlan::Batch(64)); + } +} diff --git a/codex-rs/tui/src/streaming/commit_tick.rs b/codex-rs/tui/src/streaming/commit_tick.rs new file mode 100644 index 0000000000..bda63bccf6 --- /dev/null +++ b/codex-rs/tui/src/streaming/commit_tick.rs @@ -0,0 +1,224 @@ +//! Orchestrates commit-tick drains across streaming controllers. +//! +//! This module bridges queue-based chunking policy (`chunking`) with the concrete stream +//! controllers (`controller`). Callers provide the current controllers and tick scope; the module +//! computes queue pressure, selects a drain plan, applies it, and returns emitted history cells. +//! +//! The module preserves ordering by draining only from controller queue heads. It does not schedule +//! ticks and it does not mutate UI state directly; callers remain responsible for animation events +//! and history insertion side effects. +//! +//! The main flow is: +//! [`run_commit_tick`] -> [`stream_queue_snapshot`] -> [`QueueSnapshot`] -> +//! [`resolve_chunking_plan`] -> [`ChunkingDecision`]/[`DrainPlan`] -> +//! [`apply_commit_tick_plan`] -> [`CommitTickOutput`]. + +use std::time::Duration; +use std::time::Instant; + +use crate::history_cell::HistoryCell; + +use super::chunking::AdaptiveChunkingPolicy; +use super::chunking::ChunkingDecision; +use super::chunking::ChunkingMode; +use super::chunking::DrainPlan; +use super::chunking::QueueSnapshot; +use super::controller::PlanStreamController; +use super::controller::StreamController; + +/// Describes whether a commit tick may run in all modes or only in catch-up mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum CommitTickScope { + /// Always run the tick, regardless of current chunking mode. + AnyMode, + /// Run model transitions and policy updates, but commit lines only in `CatchUp`. + CatchUpOnly, +} + +/// Describes what a single commit tick produced. +pub(crate) struct CommitTickOutput { + /// Cells produced by drained stream lines during this tick. + pub(crate) cells: Vec>, + /// Whether at least one stream controller was present for this tick. + pub(crate) has_controller: bool, + /// Whether all present controllers were idle after this tick. + pub(crate) all_idle: bool, +} + +impl Default for CommitTickOutput { + /// Creates an output that represents "no commit performed". + /// + /// This is used when a tick is intentionally suppressed, for example when + /// the scope is [`CommitTickScope::CatchUpOnly`] and policy is not in catch-up mode. + fn default() -> Self { + Self { + cells: Vec::new(), + has_controller: false, + all_idle: true, + } + } +} + +/// Runs one commit tick against the provided stream controllers. +/// +/// This function collects a [`QueueSnapshot`], asks [`AdaptiveChunkingPolicy`] for a +/// [`ChunkingDecision`], and then applies the resulting [`DrainPlan`] to both controllers. +/// If callers pass stale controller references (for example, references not tied to the +/// current turn), queue age can be misread and the policy may stay in catch-up longer +/// than expected. +pub(crate) fn run_commit_tick( + policy: &mut AdaptiveChunkingPolicy, + stream_controller: Option<&mut StreamController>, + plan_stream_controller: Option<&mut PlanStreamController>, + scope: CommitTickScope, + now: Instant, +) -> CommitTickOutput { + let snapshot = stream_queue_snapshot( + stream_controller.as_deref(), + plan_stream_controller.as_deref(), + now, + ); + let decision = resolve_chunking_plan(policy, snapshot, now); + if scope == CommitTickScope::CatchUpOnly && decision.mode != ChunkingMode::CatchUp { + return CommitTickOutput::default(); + } + + let output = apply_commit_tick_plan( + decision.drain_plan, + stream_controller, + plan_stream_controller, + ); + tracing::trace!( + mode = ?decision.mode, + queued_lines = snapshot.queued_lines, + oldest_queued_age_ms = snapshot.oldest_age.map(|age| age.as_millis() as u64), + drain_plan = ?decision.drain_plan, + has_controller = output.has_controller, + all_idle = output.all_idle, + "stream chunking commit tick" + ); + output +} + +/// Builds the combined queue-pressure snapshot consumed by chunking policy. +/// +/// The snapshot sums queue depth across controllers and keeps the maximum oldest age +/// so policy decisions reflect the most delayed queued line currently visible. +fn stream_queue_snapshot( + stream_controller: Option<&StreamController>, + plan_stream_controller: Option<&PlanStreamController>, + now: Instant, +) -> QueueSnapshot { + let mut queued_lines = 0usize; + let mut oldest_age: Option = None; + + if let Some(controller) = stream_controller { + queued_lines += controller.queued_lines(); + oldest_age = max_duration(oldest_age, controller.oldest_queued_age(now)); + } + if let Some(controller) = plan_stream_controller { + queued_lines += controller.queued_lines(); + oldest_age = max_duration(oldest_age, controller.oldest_queued_age(now)); + } + + QueueSnapshot { + queued_lines, + oldest_age, + } +} + +/// Computes one policy decision and emits a trace log on mode transitions. +/// +/// This keeps policy transition logging in one place so callers can rely on +/// [`run_commit_tick`] to provide consistent observability. +fn resolve_chunking_plan( + policy: &mut AdaptiveChunkingPolicy, + snapshot: QueueSnapshot, + now: Instant, +) -> ChunkingDecision { + let prior_mode = policy.mode(); + let decision = policy.decide(snapshot, now); + if decision.mode != prior_mode { + tracing::trace!( + prior_mode = ?prior_mode, + new_mode = ?decision.mode, + queued_lines = snapshot.queued_lines, + oldest_queued_age_ms = snapshot.oldest_age.map(|age| age.as_millis() as u64), + entered_catch_up = decision.entered_catch_up, + "stream chunking mode transition" + ); + } + decision +} + +/// Applies a [`DrainPlan`] to all available stream controllers. +/// +/// The returned [`CommitTickOutput`] reports emitted cells and whether all +/// present controllers are idle after draining. +fn apply_commit_tick_plan( + drain_plan: DrainPlan, + stream_controller: Option<&mut StreamController>, + plan_stream_controller: Option<&mut PlanStreamController>, +) -> CommitTickOutput { + let mut output = CommitTickOutput::default(); + + if let Some(controller) = stream_controller { + output.has_controller = true; + let (cell, is_idle) = drain_stream_controller(controller, drain_plan); + if let Some(cell) = cell { + output.cells.push(cell); + } + output.all_idle &= is_idle; + } + if let Some(controller) = plan_stream_controller { + output.has_controller = true; + let (cell, is_idle) = drain_plan_stream_controller(controller, drain_plan); + if let Some(cell) = cell { + output.cells.push(cell); + } + output.all_idle &= is_idle; + } + + output +} + +/// Applies one drain step to the main stream controller. +/// +/// [`DrainPlan::Single`] maps to one-line drain; [`DrainPlan::Batch`] maps to +/// multi-line drain (including instant catch-up when policy requests the full +/// queued backlog). +fn drain_stream_controller( + controller: &mut StreamController, + drain_plan: DrainPlan, +) -> (Option>, bool) { + match drain_plan { + DrainPlan::Single => controller.on_commit_tick(), + DrainPlan::Batch(max_lines) => controller.on_commit_tick_batch(max_lines), + } +} + +/// Applies one drain step to the plan stream controller. +/// +/// This mirrors [`drain_stream_controller`] so both controller types follow the +/// same chunking policy decisions. +fn drain_plan_stream_controller( + controller: &mut PlanStreamController, + drain_plan: DrainPlan, +) -> (Option>, bool) { + match drain_plan { + DrainPlan::Single => controller.on_commit_tick(), + DrainPlan::Batch(max_lines) => controller.on_commit_tick_batch(max_lines), + } +} + +/// Returns the greater of two optional durations. +/// +/// This helper preserves whichever side is present when only one duration exists. +fn max_duration(lhs: Option, rhs: Option) -> Option { + match (lhs, rhs) { + (Some(left), Some(right)) => Some(left.max(right)), + (Some(left), None) => Some(left), + (None, Some(right)) => Some(right), + (None, None) => None, + } +} diff --git a/codex-rs/tui/src/streaming/controller.rs b/codex-rs/tui/src/streaming/controller.rs index 462962e980..6117485adf 100644 --- a/codex-rs/tui/src/streaming/controller.rs +++ b/codex-rs/tui/src/streaming/controller.rs @@ -4,6 +4,8 @@ use crate::render::line_utils::prefix_lines; use crate::style::proposed_plan_style; use ratatui::prelude::Stylize; use ratatui::text::Line; +use std::time::Duration; +use std::time::Instant; use super::StreamState; @@ -71,6 +73,28 @@ impl StreamController { (self.emit(step), self.state.is_idle()) } + /// Step animation: commit at most `max_lines` queued lines. + /// + /// This is intended for adaptive catch-up drains. Callers should keep `max_lines` bounded; a + /// very large value can collapse perceived animation into a single jump. + pub(crate) fn on_commit_tick_batch( + &mut self, + max_lines: usize, + ) -> (Option>, bool) { + let step = self.state.drain_n(max_lines.max(1)); + (self.emit(step), self.state.is_idle()) + } + + /// Returns the current number of queued lines waiting to be displayed. + pub(crate) fn queued_lines(&self) -> usize { + self.state.queued_len() + } + + /// Returns the age of the oldest queued line. + pub(crate) fn oldest_queued_age(&self, now: Instant) -> Option { + self.state.oldest_queued_age(now) + } + fn emit(&mut self, lines: Vec>) -> Option> { if lines.is_empty() { return None; @@ -142,6 +166,28 @@ impl PlanStreamController { (self.emit(step, false), self.state.is_idle()) } + /// Step animation: commit at most `max_lines` queued lines. + /// + /// This is intended for adaptive catch-up drains. Callers should keep `max_lines` bounded; a + /// very large value can collapse perceived animation into a single jump. + pub(crate) fn on_commit_tick_batch( + &mut self, + max_lines: usize, + ) -> (Option>, bool) { + let step = self.state.drain_n(max_lines.max(1)); + (self.emit(step, false), self.state.is_idle()) + } + + /// Returns the current number of queued plan lines waiting to be displayed. + pub(crate) fn queued_lines(&self) -> usize { + self.state.queued_len() + } + + /// Returns the age of the oldest queued plan line. + pub(crate) fn oldest_queued_age(&self, now: Instant) -> Option { + self.state.oldest_queued_age(now) + } + fn emit( &mut self, lines: Vec>, diff --git a/codex-rs/tui/src/streaming/mod.rs b/codex-rs/tui/src/streaming/mod.rs index fa00702834..c783f27ae9 100644 --- a/codex-rs/tui/src/streaming/mod.rs +++ b/codex-rs/tui/src/streaming/mod.rs @@ -1,17 +1,39 @@ +//! Streaming primitives used by the TUI transcript pipeline. +//! +//! `StreamState` owns newline-gated markdown collection and a FIFO queue of committed render lines. +//! Higher-level modules build on top of this state: +//! - `controller` adapts queued lines into `HistoryCell` emission rules for message and plan streams. +//! - `chunking` computes adaptive drain plans from queue pressure. +//! - `commit_tick` binds policy decisions to concrete controller drains. +//! +//! The key invariant is queue ordering. All drains pop from the front, and enqueue records an +//! arrival timestamp so policy code can reason about oldest queued age without peeking into text. + use std::collections::VecDeque; +use std::time::Duration; +use std::time::Instant; use ratatui::text::Line; use crate::markdown_stream::MarkdownStreamCollector; +pub(crate) mod chunking; +pub(crate) mod commit_tick; pub(crate) mod controller; +struct QueuedLine { + line: Line<'static>, + enqueued_at: Instant, +} + +/// Holds in-flight markdown stream state and queued committed lines. pub(crate) struct StreamState { pub(crate) collector: MarkdownStreamCollector, - queued_lines: VecDeque>, + queued_lines: VecDeque, pub(crate) has_seen_delta: bool, } impl StreamState { + /// Creates an empty stream state with an optional target wrap width. pub(crate) fn new(width: Option) -> Self { Self { collector: MarkdownStreamCollector::new(width), @@ -19,21 +41,75 @@ impl StreamState { has_seen_delta: false, } } + /// Resets collector and queue state for the next stream lifecycle. pub(crate) fn clear(&mut self) { self.collector.clear(); self.queued_lines.clear(); self.has_seen_delta = false; } + /// Drains one queued line from the front of the queue. pub(crate) fn step(&mut self) -> Vec> { - self.queued_lines.pop_front().into_iter().collect() + self.queued_lines + .pop_front() + .map(|queued| queued.line) + .into_iter() + .collect() } + /// Drains up to `max_lines` queued lines from the front of the queue. + /// + /// Callers that pass very large values still get bounded behavior because this method clamps to + /// the currently available queue length. + pub(crate) fn drain_n(&mut self, max_lines: usize) -> Vec> { + let end = max_lines.min(self.queued_lines.len()); + self.queued_lines + .drain(..end) + .map(|queued| queued.line) + .collect() + } + /// Drains all queued lines from the front of the queue. pub(crate) fn drain_all(&mut self) -> Vec> { - self.queued_lines.drain(..).collect() + self.queued_lines + .drain(..) + .map(|queued| queued.line) + .collect() } + /// Returns whether no lines are queued for commit. pub(crate) fn is_idle(&self) -> bool { self.queued_lines.is_empty() } + /// Returns the current queue depth. + pub(crate) fn queued_len(&self) -> usize { + self.queued_lines.len() + } + /// Returns the age of the oldest queued line. + pub(crate) fn oldest_queued_age(&self, now: Instant) -> Option { + self.queued_lines + .front() + .map(|queued| now.saturating_duration_since(queued.enqueued_at)) + } + /// Appends committed lines to the queue with a shared enqueue timestamp. pub(crate) fn enqueue(&mut self, lines: Vec>) { - self.queued_lines.extend(lines); + let now = Instant::now(); + self.queued_lines + .extend(lines.into_iter().map(|line| QueuedLine { + line, + enqueued_at: now, + })); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn drain_n_clamps_to_available_lines() { + let mut state = StreamState::new(None); + state.enqueue(vec![Line::from("one")]); + + let drained = state.drain_n(8); + assert_eq!(drained, vec![Line::from("one")]); + assert!(state.is_idle()); } } diff --git a/codex-rs/tui/src/text_formatting.rs b/codex-rs/tui/src/text_formatting.rs index f747b2fef2..4869645dc0 100644 --- a/codex-rs/tui/src/text_formatting.rs +++ b/codex-rs/tui/src/text_formatting.rs @@ -327,6 +327,33 @@ pub(crate) fn center_truncate_path(path: &str, max_width: usize) -> String { front_truncate(path, max_width) } +/// Join a list of strings with proper English punctuation. +/// Examples: +/// - [] -> "" +/// - ["apple"] -> "apple" +/// - ["apple", "banana"] -> "apple and banana" +/// - ["apple", "banana", "cherry"] -> "apple, banana and cherry" +pub(crate) fn proper_join>(items: &[T]) -> String { + match items.len() { + 0 => String::new(), + 1 => items[0].as_ref().to_string(), + 2 => format!("{} and {}", items[0].as_ref(), items[1].as_ref()), + _ => { + let last = items[items.len() - 1].as_ref(); + let mut result = String::new(); + + for (i, item) in items.iter().take(items.len() - 1).enumerate() { + if i > 0 { + result.push_str(", "); + } + result.push_str(item.as_ref()); + } + + format!("{result} and {last}") + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -534,4 +561,20 @@ mod tests { assert_eq!(format_json_compact("null").unwrap(), "null"); assert_eq!(format_json_compact(r#""string""#).unwrap(), r#""string""#); } + + #[test] + fn test_proper_join() { + let empty: Vec = vec![]; + assert_eq!(proper_join(&empty), ""); + assert_eq!(proper_join(&["apple"]), "apple"); + assert_eq!(proper_join(&["apple", "banana"]), "apple and banana"); + assert_eq!( + proper_join(&["apple", "banana", "cherry"]), + "apple, banana and cherry" + ); + assert_eq!( + proper_join(&["apple", "banana", "cherry", "date"]), + "apple, banana, cherry and date" + ); + } } diff --git a/codex-rs/tui/src/tooltips.rs b/codex-rs/tui/src/tooltips.rs index b62831e7e6..a9dbd82e2f 100644 --- a/codex-rs/tui/src/tooltips.rs +++ b/codex-rs/tui/src/tooltips.rs @@ -6,9 +6,13 @@ use rand::Rng; const ANNOUNCEMENT_TIP_URL: &str = "https://raw.githubusercontent.com/openai/codex/main/announcement_tip.toml"; +const IS_MACOS: bool = cfg!(target_os = "macos"); + const PAID_TOOLTIP: &str = "*New* Try the **Codex App** with 2x rate limits until *April 2nd*. Run 'codex app' or visit https://chatgpt.com/codex"; +const PAID_TOOLTIP_NON_MAC: &str = "*New* 2x rate limits until *April 2nd*."; const OTHER_TOOLTIP: &str = "*New* Build faster with the **Codex App**. Run 'codex app' or visit https://chatgpt.com/codex"; +const OTHER_TOOLTIP_NON_MAC: &str = "*New* Build faster with Codex."; const FREE_GO_TOOLTIP: &str = "*New* Codex is included in your plan for free through *March 2nd* – let’s build together."; @@ -18,7 +22,15 @@ lazy_static! { static ref TOOLTIPS: Vec<&'static str> = RAW_TOOLTIPS .lines() .map(str::trim) - .filter(|line| !line.is_empty() && !line.starts_with('#')) + .filter(|line| { + if line.is_empty() || line.starts_with('#') { + return false; + } + if !IS_MACOS && line.contains("codex app") { + return false; + } + true + }) .collect(); static ref ALL_TOOLTIPS: Vec<&'static str> = { let mut tips = Vec::new(); @@ -39,6 +51,10 @@ fn experimental_tooltips() -> Vec<&'static str> { pub(crate) fn get_tooltip(plan: Option) -> Option { let mut rng = rand::rng(); + if let Some(announcement) = announcement::fetch_announcement_tip() { + return Some(announcement); + } + // Leave small chance for a random tooltip to be shown. if rng.random_ratio(8, 10) { match plan { @@ -47,19 +63,27 @@ pub(crate) fn get_tooltip(plan: Option) -> Option { | Some(PlanType::Team) | Some(PlanType::Enterprise) | Some(PlanType::Pro) => { - return Some(PAID_TOOLTIP.to_string()); + let tooltip = if IS_MACOS { + PAID_TOOLTIP + } else { + PAID_TOOLTIP_NON_MAC + }; + return Some(tooltip.to_string()); } Some(PlanType::Go) | Some(PlanType::Free) => { return Some(FREE_GO_TOOLTIP.to_string()); } - _ => return Some(OTHER_TOOLTIP.to_string()), + _ => { + let tooltip = if IS_MACOS { + OTHER_TOOLTIP + } else { + OTHER_TOOLTIP_NON_MAC + }; + return Some(tooltip.to_string()); + } } } - if let Some(announcement) = announcement::fetch_announcement_tip() { - return Some(announcement); - } - pick_tooltip(&mut rng).map(str::to_string) } diff --git a/codex-rs/tui/src/tui.rs b/codex-rs/tui/src/tui.rs index 761fa83627..ab9c88d2ae 100644 --- a/codex-rs/tui/src/tui.rs +++ b/codex-rs/tui/src/tui.rs @@ -10,6 +10,7 @@ use std::pin::Pin; use std::sync::Arc; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; +use std::time::Duration; use crossterm::Command; use crossterm::SynchronizedUpdate; @@ -52,6 +53,9 @@ mod frame_requester; #[cfg(unix)] mod job_control; +/// Target frame interval for UI redraw scheduling. +pub(crate) const TARGET_FRAME_INTERVAL: Duration = frame_rate_limiter::MIN_FRAME_INTERVAL; + /// A type alias for the terminal type used in this application pub type Terminal = CustomTerminal>; @@ -210,6 +214,8 @@ pub fn init() -> Result { } set_modes()?; + flush_terminal_input_buffer(); + set_panic_hook(); let backend = CrosstermBackend::new(stdout()); diff --git a/codex-rs/tui/src/tui/frame_rate_limiter.rs b/codex-rs/tui/src/tui/frame_rate_limiter.rs index 56dd752e64..6f4ad8e747 100644 --- a/codex-rs/tui/src/tui/frame_rate_limiter.rs +++ b/codex-rs/tui/src/tui/frame_rate_limiter.rs @@ -1,7 +1,7 @@ //! Limits how frequently frame draw notifications may be emitted. //! //! Widgets sometimes call `FrameRequester::schedule_frame()` more frequently than a user can -//! perceive. This limiter clamps draw notifications to a maximum of 60 FPS to avoid wasted work. +//! perceive. This limiter clamps draw notifications to a maximum of 120 FPS to avoid wasted work. //! //! This is intentionally a small, pure helper so it can be unit-tested in isolation and used by //! the async frame scheduler without adding complexity to the app/event loop. @@ -9,8 +9,8 @@ use std::time::Duration; use std::time::Instant; -/// A 60 FPS minimum frame interval (≈16.67ms). -pub(super) const MIN_FRAME_INTERVAL: Duration = Duration::from_nanos(16_666_667); +/// A 120 FPS minimum frame interval (≈8.33ms). +pub(super) const MIN_FRAME_INTERVAL: Duration = Duration::from_nanos(8_333_334); /// Remembers the most recent emitted draw, allowing deadlines to be clamped forward. #[derive(Debug, Default)] diff --git a/codex-rs/tui/src/tui/frame_requester.rs b/codex-rs/tui/src/tui/frame_requester.rs index 88db9d71f3..d7e54d82cc 100644 --- a/codex-rs/tui/src/tui/frame_requester.rs +++ b/codex-rs/tui/src/tui/frame_requester.rs @@ -71,7 +71,7 @@ impl FrameRequester { /// /// This type is internal to `FrameRequester` and is spawned as a task to handle scheduling logic. /// -/// To avoid wasted redraw work, draw notifications are clamped to a maximum of 60 FPS (see +/// To avoid wasted redraw work, draw notifications are clamped to a maximum of 120 FPS (see /// [`FrameRateLimiter`]). struct FrameScheduler { receiver: mpsc::UnboundedReceiver, @@ -232,7 +232,7 @@ mod tests { } #[tokio::test(flavor = "current_thread", start_paused = true)] - async fn test_limits_draw_notifications_to_60fps() { + async fn test_limits_draw_notifications_to_120fps() { let (draw_tx, mut draw_rx) = broadcast::channel(16); let requester = FrameRequester::new(draw_tx); @@ -250,7 +250,7 @@ mod tests { let early = draw_rx.recv().timeout(Duration::from_millis(1)).await; assert!( early.is_err(), - "draw fired too early; expected max 60fps (min interval {MIN_FRAME_INTERVAL:?})" + "draw fired too early; expected max 120fps (min interval {MIN_FRAME_INTERVAL:?})" ); time::advance(MIN_FRAME_INTERVAL).await; @@ -278,11 +278,11 @@ mod tests { requester.schedule_frame_in(Duration::from_millis(1)); - time::advance(Duration::from_millis(10)).await; + time::advance(MIN_FRAME_INTERVAL / 2).await; let too_early = draw_rx.recv().timeout(Duration::from_millis(1)).await; assert!( too_early.is_err(), - "draw fired too early; expected max 60fps (min interval {MIN_FRAME_INTERVAL:?})" + "draw fired too early; expected max 120fps (min interval {MIN_FRAME_INTERVAL:?})" ); time::advance(MIN_FRAME_INTERVAL).await; diff --git a/codex-rs/tui/src/updates.rs b/codex-rs/tui/src/updates.rs index 89fd6f32f6..58d025a4a1 100644 --- a/codex-rs/tui/src/updates.rs +++ b/codex-rs/tui/src/updates.rs @@ -56,8 +56,7 @@ struct VersionInfo { const VERSION_FILENAME: &str = "version.json"; // We use the latest version from the cask if installation is via homebrew - homebrew does not immediately pick up the latest release and can lag behind. -const HOMEBREW_CASK_URL: &str = - "https://raw.githubusercontent.com/Homebrew/homebrew-cask/HEAD/Casks/c/codex.rb"; +const HOMEBREW_CASK_API_URL: &str = "https://formulae.brew.sh/api/cask/codex.json"; const LATEST_RELEASE_URL: &str = "https://api.github.com/repos/openai/codex/releases/latest"; #[derive(Deserialize, Debug, Clone)] @@ -65,6 +64,11 @@ struct ReleaseInfo { tag_name: String, } +#[derive(Deserialize, Debug, Clone)] +struct HomebrewCaskInfo { + version: String, +} + fn version_filepath(config: &Config) -> PathBuf { config.codex_home.join(VERSION_FILENAME) } @@ -77,14 +81,14 @@ fn read_version_info(version_file: &Path) -> anyhow::Result { async fn check_for_update(version_file: &Path) -> anyhow::Result<()> { let latest_version = match update_action::get_update_action() { Some(UpdateAction::BrewUpgrade) => { - let cask_contents = create_client() - .get(HOMEBREW_CASK_URL) + let HomebrewCaskInfo { version } = create_client() + .get(HOMEBREW_CASK_API_URL) .send() .await? .error_for_status()? - .text() + .json::() .await?; - extract_version_from_cask(&cask_contents)? + version } _ => { let ReleaseInfo { @@ -123,18 +127,6 @@ fn is_newer(latest: &str, current: &str) -> Option { } } -fn extract_version_from_cask(cask_contents: &str) -> anyhow::Result { - cask_contents - .lines() - .find_map(|line| { - let line = line.trim(); - line.strip_prefix("version \"") - .and_then(|rest| rest.strip_suffix('"')) - .map(ToString::to_string) - }) - .ok_or_else(|| anyhow::anyhow!("Failed to find version in Homebrew cask file")) -} - fn extract_version_from_latest_tag(latest_tag_name: &str) -> anyhow::Result { latest_tag_name .strip_prefix("rust-v") @@ -190,16 +182,18 @@ mod tests { use super::*; #[test] - fn parses_version_from_cask_contents() { - let cask = r#" - cask "codex" do - version "0.55.0" - end - "#; - assert_eq!( - extract_version_from_cask(cask).expect("failed to parse version"), - "0.55.0" - ); + fn extract_version_from_brew_api_json() { + // + // https://formulae.brew.sh/api/cask/codex.json + let cask_json = r#"{ + "token": "codex", + "full_token": "codex", + "tap": "homebrew/cask", + "version": "0.96.0", + }"#; + let HomebrewCaskInfo { version } = serde_json::from_str::(cask_json) + .expect("failed to parse version from cask json"); + assert_eq!(version, "0.96.0"); } #[test] diff --git a/codex-rs/utils/string/Cargo.toml b/codex-rs/utils/string/Cargo.toml index 29437eaefc..8a62539382 100644 --- a/codex-rs/utils/string/Cargo.toml +++ b/codex-rs/utils/string/Cargo.toml @@ -6,3 +6,6 @@ license.workspace = true [lints] workspace = true + +[dev-dependencies] +pretty_assertions = { workspace = true } diff --git a/codex-rs/utils/string/src/lib.rs b/codex-rs/utils/string/src/lib.rs index f7299d4372..b69f185649 100644 --- a/codex-rs/utils/string/src/lib.rs +++ b/codex-rs/utils/string/src/lib.rs @@ -36,3 +36,46 @@ pub fn take_last_bytes_at_char_boundary(s: &str, maxb: usize) -> &str { } &s[start..] } + +/// Sanitize a tag value to comply with metric tag validation rules: +/// only ASCII alphanumeric, '.', '_', '-', and '/' are allowed. +pub fn sanitize_metric_tag_value(value: &str) -> String { + const MAX_LEN: usize = 256; + let sanitized: String = value + .chars() + .map(|ch| { + if ch.is_ascii_alphanumeric() || matches!(ch, '.' | '_' | '-' | '/') { + ch + } else { + '_' + } + }) + .collect(); + let trimmed = sanitized.trim_matches('_'); + if trimmed.is_empty() || trimmed.chars().all(|ch| !ch.is_ascii_alphanumeric()) { + return "unspecified".to_string(); + } + if trimmed.len() <= MAX_LEN { + trimmed.to_string() + } else { + trimmed[..MAX_LEN].to_string() + } +} + +#[cfg(test)] +mod tests { + use super::sanitize_metric_tag_value; + use pretty_assertions::assert_eq; + + #[test] + fn sanitize_metric_tag_value_trims_and_fills_unspecified() { + let msg = "///"; + assert_eq!(sanitize_metric_tag_value(msg), "unspecified"); + } + + #[test] + fn sanitize_metric_tag_value_replaces_invalid_chars() { + let msg = "bad value!"; + assert_eq!(sanitize_metric_tag_value(msg), "bad_value"); + } +} diff --git a/codex-rs/windows-sandbox-rs/src/lib.rs b/codex-rs/windows-sandbox-rs/src/lib.rs index b938a86e6b..4a97efaaf8 100644 --- a/codex-rs/windows-sandbox-rs/src/lib.rs +++ b/codex-rs/windows-sandbox-rs/src/lib.rs @@ -91,7 +91,7 @@ pub use setup::SETUP_VERSION; #[cfg(target_os = "windows")] pub use setup_error::extract_failure as extract_setup_failure; #[cfg(target_os = "windows")] -pub use setup_error::sanitize_tag_value as sanitize_setup_metric_tag_value; +pub use setup_error::sanitize_setup_metric_tag_value; #[cfg(target_os = "windows")] pub use setup_error::setup_error_path; #[cfg(target_os = "windows")] diff --git a/codex-rs/windows-sandbox-rs/src/setup_error.rs b/codex-rs/windows-sandbox-rs/src/setup_error.rs index 9ee9ebf7f5..23022450a0 100644 --- a/codex-rs/windows-sandbox-rs/src/setup_error.rs +++ b/codex-rs/windows-sandbox-rs/src/setup_error.rs @@ -1,5 +1,6 @@ use anyhow::Context; use anyhow::Result; +use codex_utils_string::sanitize_metric_tag_value; use serde::Deserialize; use serde::Serialize; use std::fs; @@ -126,7 +127,7 @@ impl SetupFailure { } pub fn metric_message(&self) -> String { - sanitize_tag_value(&self.message) + sanitize_setup_metric_tag_value(&self.message) } } @@ -181,30 +182,9 @@ pub fn read_setup_error_report(codex_home: &Path) -> Result String { - const MAX_LEN: usize = 256; - let redacted = redact_home_paths(value); - let sanitized: String = redacted - .chars() - .map(|ch| { - if ch.is_ascii_alphanumeric() || matches!(ch, '.' | '_' | '-' | '/') { - ch - } else { - '_' - } - }) - .collect(); - let trimmed = sanitized.trim_matches('_'); - if trimmed.is_empty() { - return "unspecified".to_string(); - } - if trimmed.len() <= MAX_LEN { - trimmed.to_string() - } else { - trimmed[..MAX_LEN].to_string() - } +/// Sanitize a setup error message for use as a metric tag. +pub fn sanitize_setup_metric_tag_value(value: &str) -> String { + sanitize_metric_tag_value(redact_home_paths(value).as_str()) } fn redact_home_paths(value: &str) -> String { @@ -267,7 +247,7 @@ fn redact_username_segments(value: &str, usernames: &[String]) -> String { #[cfg(test)] mod tests { - use super::*; + use super::redact_username_segments; use pretty_assertions::assert_eq; #[test] diff --git a/docs/install.md b/docs/install.md index 20d8b54a54..ef14f5d840 100644 --- a/docs/install.md +++ b/docs/install.md @@ -51,7 +51,7 @@ cargo test --all-features Codex is written in Rust, so it honors the `RUST_LOG` environment variable to configure its logging behavior. -The TUI defaults to `RUST_LOG=codex_core=info,codex_tui=info,codex_rmcp_client=info` and log messages are written to `~/.codex/log/codex-tui.log`, so you can leave the following running in a separate terminal to monitor log messages as they are written: +The TUI defaults to `RUST_LOG=codex_core=info,codex_tui=info,codex_rmcp_client=info` and log messages are written to `~/.codex/log/codex-tui.log` by default. For a single run, you can override the log directory with `-c log_dir=...` (for example, `-c log_dir=./.codex-log`). ```bash tail -F ~/.codex/log/codex-tui.log diff --git a/docs/tui-request-user-input.md b/docs/tui-request-user-input.md index f8eb7aeb73..8ca6f5369b 100644 --- a/docs/tui-request-user-input.md +++ b/docs/tui-request-user-input.md @@ -30,7 +30,9 @@ friction for freeform input. - Enter advances to the next question. - Enter on the last question submits all answers. - PageUp/PageDown navigate across questions (when multiple are present). -- Esc interrupts the run. +- Esc interrupts the run in option selection mode. +- When notes are open for an option question, Tab or Esc clears notes and returns + to option selection. ## Layout priorities diff --git a/docs/tui-stream-chunking-review.md b/docs/tui-stream-chunking-review.md new file mode 100644 index 0000000000..3722492ddf --- /dev/null +++ b/docs/tui-stream-chunking-review.md @@ -0,0 +1,124 @@ +# TUI Stream Chunking + +This document explains how stream chunking in the TUI works and why it is +implemented this way. + +## Problem + +Streaming output can arrive faster than a one-line-per-tick animation can show +it. If commit speed stays fixed while arrival speed spikes, queued lines grow +and visible output lags behind received output. + +## Design goals + +- Preserve existing baseline behavior under normal load. +- Reduce display lag when backlog builds. +- Keep output order stable. +- Avoid abrupt single-frame flushes that look jumpy. +- Keep policy transport-agnostic and based only on queue state. + +## Non-goals + +- The policy does not schedule animation ticks. +- The policy does not depend on upstream source identity. +- The policy does not reorder queued output. + +## Where the logic lives + +- `codex-rs/tui/src/streaming/chunking.rs` + - Adaptive policy, mode transitions, and drain-plan selection. +- `codex-rs/tui/src/streaming/commit_tick.rs` + - Orchestration for each commit tick: snapshot, decide, drain, trace. +- `codex-rs/tui/src/streaming/controller.rs` + - Queue/drain primitives used by commit-tick orchestration. +- `codex-rs/tui/src/chatwidget.rs` + - Integration point that invokes commit-tick orchestration and handles UI + lifecycle events. + +## Runtime flow + +On each commit tick: + +1. Build a queue snapshot across active controllers. + - `queued_lines`: total queued lines. + - `oldest_age`: max age of the oldest queued line across controllers. +2. Ask adaptive policy for a decision. + - Output: current mode and a drain plan. +3. Apply drain plan to each controller. +4. Emit drained `HistoryCell`s for insertion by the caller. +5. Emit trace logs for observability. + +In `CatchUpOnly` scope, policy state still advances, but draining is skipped +unless mode is currently `CatchUp`. + +## Modes and transitions + +Two modes are used: + +- `Smooth` + - Baseline behavior: one line drained per baseline commit tick. + - Baseline tick interval currently comes from + `tui/src/app.rs:COMMIT_ANIMATION_TICK` (~8.3ms, ~120fps). +- `CatchUp` + - Drain current queued backlog per tick via `Batch(queued_lines)`. + +Entry and exit use hysteresis: + +- Enter `CatchUp` when queue depth or queue age exceeds enter thresholds. +- Exit requires both depth and age to be below exit thresholds for a hold + window (`EXIT_HOLD`). + +This prevents oscillation when load hovers near thresholds. + +## Current experimental tuning values + +These are the current values in `streaming/chunking.rs` plus the baseline +commit tick in `tui/src/app.rs`. They are +experimental and may change as we gather more trace data. + +- Baseline commit tick: `~8.3ms` (`COMMIT_ANIMATION_TICK` in `app.rs`) +- Enter catch-up: + - `queued_lines >= 8` OR `oldest_age >= 120ms` +- Exit catch-up eligibility: + - `queued_lines <= 2` AND `oldest_age <= 40ms` +- Exit hold (`CatchUp -> Smooth`): `250ms` +- Re-entry hold after catch-up exit: `250ms` +- Severe backlog thresholds: + - `queued_lines >= 64` OR `oldest_age >= 300ms` + +## Drain planning + +In `Smooth`, plan is always `Single`. + +In `CatchUp`, plan is `Batch(queued_lines)`, which drains the currently queued +backlog for immediate convergence. + +## Why this design + +This keeps normal animation semantics intact, while making backlog behavior +adaptive: + +- Under normal load, behavior stays familiar and stable. +- Under pressure, queue age is reduced quickly without sacrificing ordering. +- Hysteresis avoids rapid mode flapping. + +## Invariants + +- Queue order is preserved. +- Empty queue resets policy back to `Smooth`. +- `CatchUp` exits only after sustained low pressure. +- Catch-up drains are immediate while in `CatchUp`. + +## Observability + +Trace events are emitted from commit-tick orchestration: + +- `stream chunking commit tick` + - `mode`, `queued_lines`, `oldest_queued_age_ms`, `drain_plan`, + `has_controller`, `all_idle` +- `stream chunking mode transition` + - `prior_mode`, `new_mode`, `queued_lines`, `oldest_queued_age_ms`, + `entered_catch_up` + +These events are intended to explain display lag by showing queue pressure, +selected drain behavior, and mode transitions over time. diff --git a/docs/tui-stream-chunking-tuning.md b/docs/tui-stream-chunking-tuning.md new file mode 100644 index 0000000000..d9a2ea5e21 --- /dev/null +++ b/docs/tui-stream-chunking-tuning.md @@ -0,0 +1,98 @@ +# TUI Stream Chunking Tuning Guide + +This document explains how to tune adaptive stream chunking constants without +changing the underlying policy shape. + +## Scope + +Use this guide when adjusting queue-pressure thresholds and hysteresis windows in +`codex-rs/tui/src/streaming/chunking.rs`, and baseline commit cadence in +`codex-rs/tui/src/app.rs`. + +This guide is about tuning behavior, not redesigning the policy. + +## Before tuning + +- Keep the baseline behavior intact: + - `Smooth` mode drains one line per baseline tick. + - `CatchUp` mode drains queued backlog immediately. +- Capture trace logs with: + - `codex_tui::streaming::commit_tick` +- Evaluate on sustained, bursty, and mixed-output prompts. + +See `docs/tui-stream-chunking-validation.md` for the measurement process. + +## Tuning goals + +Tune for all three goals together: + +- low visible lag under bursty output +- low mode flapping (`Smooth <-> CatchUp` chatter) +- stable catch-up entry/exit behavior under mixed workloads + +## Constants and what they control + +### Baseline commit cadence + +- `COMMIT_ANIMATION_TICK` (`tui/src/app.rs`) + - Lower values increase smooth-mode update cadence and reduce steady-state lag. + - Higher values increase smoothing and can increase perceived lag. + - This should usually move after chunking thresholds/holds are in a good range. + +### Enter/exit thresholds + +- `ENTER_QUEUE_DEPTH_LINES`, `ENTER_OLDEST_AGE` + - Lower values enter catch-up earlier (less lag, more mode switching risk). + - Higher values enter later (more lag tolerance, fewer mode switches). +- `EXIT_QUEUE_DEPTH_LINES`, `EXIT_OLDEST_AGE` + - Lower values keep catch-up active longer. + - Higher values allow earlier exit and may increase re-entry churn. + +### Hysteresis holds + +- `EXIT_HOLD` + - Longer hold reduces flip-flop exits when pressure is noisy. + - Too long can keep catch-up active after pressure has cleared. +- `REENTER_CATCH_UP_HOLD` + - Longer hold suppresses rapid re-entry after exit. + - Too long can delay needed catch-up for near-term bursts. + - Severe backlog bypasses this hold by design. + +### Severe-backlog gates + +- `SEVERE_QUEUE_DEPTH_LINES`, `SEVERE_OLDEST_AGE` + - Lower values bypass re-entry hold earlier. + - Higher values reserve hold bypass for only extreme pressure. + +## Recommended tuning order + +Tune in this order to keep cause/effect clear: + +1. Entry/exit thresholds (`ENTER_*`, `EXIT_*`) +2. Hold windows (`EXIT_HOLD`, `REENTER_CATCH_UP_HOLD`) +3. Severe gates (`SEVERE_*`) +4. Baseline cadence (`COMMIT_ANIMATION_TICK`) + +Change one logical group at a time and re-measure before the next group. + +## Symptom-driven adjustments + +- Too much lag before catch-up starts: + - lower `ENTER_QUEUE_DEPTH_LINES` and/or `ENTER_OLDEST_AGE` +- Frequent `Smooth -> CatchUp -> Smooth` chatter: + - increase `EXIT_HOLD` + - increase `REENTER_CATCH_UP_HOLD` + - tighten exit thresholds (lower `EXIT_*`) +- Catch-up engages too often for short bursts: + - increase `ENTER_QUEUE_DEPTH_LINES` and/or `ENTER_OLDEST_AGE` + - increase `REENTER_CATCH_UP_HOLD` +- Catch-up engages too late: + - lower `ENTER_QUEUE_DEPTH_LINES` and/or `ENTER_OLDEST_AGE` + - lower severe gates (`SEVERE_*`) to bypass re-entry hold sooner + +## Validation checklist after each tuning pass + +- `cargo test -p codex-tui` passes. +- Trace window shows bounded queue-age behavior. +- Mode transitions are not concentrated in repeated short-interval cycles. +- Catch-up clears backlog quickly once mode enters `CatchUp`. diff --git a/docs/tui-stream-chunking-validation.md b/docs/tui-stream-chunking-validation.md new file mode 100644 index 0000000000..a31a7b6223 --- /dev/null +++ b/docs/tui-stream-chunking-validation.md @@ -0,0 +1,107 @@ +# TUI Stream Chunking Validation Process + +This document records the process used to validate adaptive stream chunking +and anti-flap behavior. + +## Scope + +The goal is to verify two properties from runtime traces: + +- display lag is reduced when queue pressure rises +- mode transitions remain stable instead of rapidly flapping + +## Trace targets + +Chunking observability is emitted by: + +- `codex_tui::streaming::commit_tick` + +Two trace messages are used: + +- `stream chunking commit tick` +- `stream chunking mode transition` + +## Runtime command + +Run Codex with chunking traces enabled: + +```bash +RUST_LOG='codex_tui::streaming::commit_tick=trace,codex_tui=info,codex_core=info,codex_rmcp_client=info' \ + just codex --enable=responses_websockets +``` + +## Log capture process + +Tip: for one-off measurements, run with `-c log_dir=...` to direct logs to a fresh directory and avoid mixing sessions. + +1. Record the current size of `~/.codex/log/codex-tui.log` as a start offset. +2. Run an interactive prompt that produces sustained streamed output. +3. Stop the run. +4. Parse only log bytes written after the recorded offset. + +This avoids mixing earlier sessions with the current measurement window. + +## Metrics reviewed + +For each measured window: + +- `commit_ticks` +- `mode_transitions` +- `smooth_ticks` +- `catchup_ticks` +- drain-plan distribution (`Single`, `Batch(n)`) +- queue depth (`max`, `p95`, `p99`) +- oldest queued age (`max`, `p95`, `p99`) +- rapid re-entry count: + - number of `Smooth -> CatchUp` transitions within 1 second of a + `CatchUp -> Smooth` transition + +## Interpretation + +- Healthy behavior: + - queue age remains bounded while backlog is drained + - transition count is low relative to total ticks + - rapid re-entry events are infrequent and localized to burst boundaries +- Regressed behavior: + - repeated short-interval mode toggles across an extended window + - persistent queue-age growth while in smooth mode + - long catch-up runs without backlog reduction + +## Experiment history + +This section captures the major tuning passes so future work can build on +what has already been tried. + +- Baseline + - One-line smooth draining with a 50ms commit tick. + - This preserved familiar pacing but could feel laggy under sustained + backlog. +- Pass 1: instant catch-up, baseline tick unchanged + - Kept smooth-mode semantics but made catch-up drain the full queued + backlog each catch-up tick. + - Result: queue lag dropped faster, but perceived motion could still feel + stepped because smooth-mode cadence remained coarse. +- Pass 2: faster baseline tick (25ms) + - Improved smooth-mode cadence and reduced visible stepping. + - Result: better, but still not aligned with draw cadence. +- Pass 3: frame-aligned baseline tick (~16.7ms) + - Set baseline commit cadence to approximately 60fps. + - Result: smoother perceived progression while retaining hysteresis and + fast backlog convergence. +- Pass 4: higher frame-aligned baseline tick (~8.3ms) + - Set baseline commit cadence to approximately 120fps. + - Result: further reduced smooth-mode stepping while preserving the same + adaptive catch-up policy shape. + +Current state combines: + +- instant catch-up draining in `CatchUp` +- hysteresis for mode-entry/exit stability +- frame-aligned smooth-mode commit cadence (~8.3ms) + +## Notes + +- Validation is source-agnostic and does not rely on naming any specific + upstream provider. +- This process intentionally preserves existing baseline smooth behavior and + focuses on burst/backlog handling behavior. diff --git a/sdk/typescript/src/exec.ts b/sdk/typescript/src/exec.ts index d569106c84..6f8048e5e4 100644 --- a/sdk/typescript/src/exec.ts +++ b/sdk/typescript/src/exec.ts @@ -115,16 +115,16 @@ export class CodexExec { commandArgs.push("--config", `approval_policy="${args.approvalPolicy}"`); } + if (args.threadId) { + commandArgs.push("resume", args.threadId); + } + if (args.images?.length) { for (const image of args.images) { commandArgs.push("--image", image); } } - if (args.threadId) { - commandArgs.push("resume", args.threadId); - } - const env: Record = {}; if (this.envOverride) { Object.assign(env, this.envOverride); diff --git a/sdk/typescript/tests/exec.test.ts b/sdk/typescript/tests/exec.test.ts index 9c4b6c2530..7ef52d72e1 100644 --- a/sdk/typescript/tests/exec.test.ts +++ b/sdk/typescript/tests/exec.test.ts @@ -67,4 +67,30 @@ describe("CodexExec", () => { expect(result.error.message).toMatch(/Codex Exec exited/); } }); + + it("places resume args before image args", async () => { + const { CodexExec } = await import("../src/exec"); + spawnMock.mockClear(); + const child = new FakeChildProcess(); + spawnMock.mockReturnValue(child as unknown as child_process.ChildProcess); + + setImmediate(() => { + child.stdout.end(); + child.stderr.end(); + child.emit("exit", 0, null); + }); + + const exec = new CodexExec("codex"); + for await (const _ of exec.run({ input: "hi", images: ["img.png"], threadId: "thread-id" })) { + // no-op + } + + const commandArgs = spawnMock.mock.calls[0]?.[1] as string[] | undefined; + expect(commandArgs).toBeDefined(); + const resumeIndex = commandArgs!.indexOf("resume"); + const imageIndex = commandArgs!.indexOf("--image"); + expect(resumeIndex).toBeGreaterThan(-1); + expect(imageIndex).toBeGreaterThan(-1); + expect(resumeIndex).toBeLessThan(imageIndex); + }); });