@corbet-labs/ccht 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +307 -0
- package/LICENSE.md +30 -0
- package/LICENSES/GPL-3.0-only.txt +232 -0
- package/LICENSES/LGPL-3.0-linking-exception.txt +16 -0
- package/LICENSES/LGPL-3.0-only WITH LGPL-3.0-linking-exception.txt +16 -0
- package/LICENSES/LGPL-3.0-only.txt +304 -0
- package/LICENSES/dependencies/agent-client-protocol-2.1.0/LICENSE +191 -0
- package/LICENSES/dependencies/agent-client-protocol-derive-2.1.0/LICENSE +191 -0
- package/LICENSES/dependencies/agent-client-protocol-schema-1.7.0/LICENSE +190 -0
- package/LICENSES/dependencies/android_system_properties-0.1.6/LICENSE-APACHE +13 -0
- package/LICENSES/dependencies/android_system_properties-0.1.6/LICENSE-MIT +20 -0
- package/LICENSES/dependencies/anyhow-1.0.104/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/anyhow-1.0.104/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/async-channel-2.5.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/async-channel-2.5.0/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/async-io-2.6.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/async-io-2.6.0/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/async-lock-3.4.2/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/async-lock-3.4.2/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/async-process-2.5.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/async-process-2.5.0/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/async-signal-0.2.14/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/async-signal-0.2.14/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/async-task-4.7.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/async-task-4.7.1/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/atomic-waker-1.1.2/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/atomic-waker-1.1.2/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/atomic-waker-1.1.2/LICENSE-THIRD-PARTY +45 -0
- package/LICENSES/dependencies/autocfg-1.5.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/autocfg-1.5.1/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/base64-0.22.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/base64-0.22.1/LICENSE-MIT +21 -0
- package/LICENSES/dependencies/base64-0.23.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/base64-0.23.1/LICENSE-MIT +21 -0
- package/LICENSES/dependencies/bitflags-1.3.2/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/bitflags-1.3.2/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/bitflags-2.13.2/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/bitflags-2.13.2/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/blocking-1.7.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/blocking-1.7.0/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/bs58-0.5.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/bs58-0.5.1/LICENSE-MIT +20 -0
- package/LICENSES/dependencies/bumpalo-3.20.3/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/bumpalo-3.20.3/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/bytes-1.12.1/LICENSE +25 -0
- package/LICENSES/dependencies/cc-1.4.5/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/cc-1.4.5/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/cfg-if-1.0.4/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/cfg-if-1.0.4/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/chrono-0.4.45/LICENSE.txt +240 -0
- package/LICENSES/dependencies/concurrent-queue-2.5.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/concurrent-queue-2.5.0/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/convert_case-0.10.0/LICENSE +21 -0
- package/LICENSES/dependencies/core-foundation-sys-0.8.7/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/core-foundation-sys-0.8.7/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/crossbeam-deque-0.8.8/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/crossbeam-deque-0.8.8/LICENSE-MIT +27 -0
- package/LICENSES/dependencies/crossbeam-epoch-0.9.21/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/crossbeam-epoch-0.9.21/LICENSE-MIT +27 -0
- package/LICENSES/dependencies/crossbeam-utils-0.8.23/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/crossbeam-utils-0.8.23/LICENSE-MIT +27 -0
- package/LICENSES/dependencies/darling-0.24.1/LICENSE +21 -0
- package/LICENSES/dependencies/darling_core-0.24.1/LICENSE +21 -0
- package/LICENSES/dependencies/darling_macro-0.24.1/LICENSE +21 -0
- package/LICENSES/dependencies/defmt-1.1.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/defmt-1.1.1/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/defmt-macros-1.1.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/defmt-macros-1.1.1/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/defmt-parser-1.0.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/defmt-parser-1.0.0/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/deranged-0.5.8/LICENSE-Apache +202 -0
- package/LICENSES/dependencies/deranged-0.5.8/LICENSE-MIT +19 -0
- package/LICENSES/dependencies/derive_more-2.1.1/LICENSE +21 -0
- package/LICENSES/dependencies/derive_more-impl-2.1.1/LICENSE +21 -0
- package/LICENSES/dependencies/dyn-clone-1.0.20/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/dyn-clone-1.0.20/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/either-1.18.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/either-1.18.0/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/equivalent-1.0.2/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/equivalent-1.0.2/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/errno-0.3.14/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/errno-0.3.14/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/event-listener-5.4.2/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/event-listener-5.4.2/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/event-listener-strategy-0.5.4/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/event-listener-strategy-0.5.4/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/fallible-iterator-0.3.0/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/fallible-iterator-0.3.0/LICENSE-MIT +19 -0
- package/LICENSES/dependencies/fastrand-2.5.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/fastrand-2.5.0/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/find-msvc-tools-0.1.12/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/find-msvc-tools-0.1.12/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/fixedbitset-0.5.7/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/fixedbitset-0.5.7/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/foldhash-0.2.0/LICENSE +19 -0
- package/LICENSES/dependencies/futures-0.3.34/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/futures-0.3.34/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/futures-channel-0.3.34/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/futures-channel-0.3.34/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/futures-concurrency-7.7.1/LICENSE-APACHE +190 -0
- package/LICENSES/dependencies/futures-concurrency-7.7.1/LICENSE-MIT +21 -0
- package/LICENSES/dependencies/futures-core-0.3.34/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/futures-core-0.3.34/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/futures-executor-0.3.34/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/futures-executor-0.3.34/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/futures-io-0.3.34/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/futures-io-0.3.34/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/futures-lite-2.6.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/futures-lite-2.6.1/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/futures-lite-2.6.1/LICENSE-THIRD-PARTY +45 -0
- package/LICENSES/dependencies/futures-macro-0.3.34/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/futures-macro-0.3.34/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/futures-sink-0.3.34/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/futures-sink-0.3.34/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/futures-task-0.3.34/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/futures-task-0.3.34/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/futures-util-0.3.34/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/futures-util-0.3.34/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/getrandom-0.4.3/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/getrandom-0.4.3/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/gimli-0.32.3/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/gimli-0.32.3/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/hashbrown-0.12.3/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/hashbrown-0.12.3/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/hashbrown-0.16.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/hashbrown-0.16.1/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/hashbrown-0.17.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/hashbrown-0.17.1/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/heck-0.5.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/heck-0.5.0/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/hermit-abi-0.5.3/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/hermit-abi-0.5.3/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/hex-0.4.3/LICENSE-APACHE +202 -0
- package/LICENSES/dependencies/hex-0.4.3/LICENSE-MIT +20 -0
- package/LICENSES/dependencies/iana-time-zone-0.1.65/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/iana-time-zone-0.1.65/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/iana-time-zone-haiku-0.1.2/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/iana-time-zone-haiku-0.1.2/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/id-arena-2.3.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/id-arena-2.3.0/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/ident_case-1.0.1/LICENSE +19 -0
- package/LICENSES/dependencies/indexmap-1.9.3/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/indexmap-1.9.3/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/indexmap-2.14.2/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/indexmap-2.14.2/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/itoa-1.0.18/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/itoa-1.0.18/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/jiff-0.2.35/COPYING +3 -0
- package/LICENSES/dependencies/jiff-0.2.35/LICENSE-MIT +21 -0
- package/LICENSES/dependencies/jiff-0.2.35/UNLICENSE +24 -0
- package/LICENSES/dependencies/jiff-core-0.1.0/COPYING +3 -0
- package/LICENSES/dependencies/jiff-core-0.1.0/LICENSE-MIT +21 -0
- package/LICENSES/dependencies/jiff-core-0.1.0/UNLICENSE +24 -0
- package/LICENSES/dependencies/jiff-static-0.2.35/COPYING +3 -0
- package/LICENSES/dependencies/jiff-static-0.2.35/LICENSE-MIT +21 -0
- package/LICENSES/dependencies/jiff-static-0.2.35/UNLICENSE +24 -0
- package/LICENSES/dependencies/jiff-tzdb-0.1.8/COPYING +3 -0
- package/LICENSES/dependencies/jiff-tzdb-0.1.8/LICENSE-MIT +21 -0
- package/LICENSES/dependencies/jiff-tzdb-0.1.8/UNLICENSE +24 -0
- package/LICENSES/dependencies/jiff-tzdb-platform-0.1.3/COPYING +3 -0
- package/LICENSES/dependencies/jiff-tzdb-platform-0.1.3/LICENSE-MIT +21 -0
- package/LICENSES/dependencies/jiff-tzdb-platform-0.1.3/UNLICENSE +24 -0
- package/LICENSES/dependencies/js-sys-0.3.105/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/js-sys-0.3.105/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/leb128-0.2.7/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/leb128-0.2.7/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/leb128fmt-0.1.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/leb128fmt-0.1.0/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/libc-0.2.189/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/libc-0.2.189/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/linux-raw-sys-0.12.1/COPYRIGHT +29 -0
- package/LICENSES/dependencies/linux-raw-sys-0.12.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/linux-raw-sys-0.12.1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/LICENSES/dependencies/linux-raw-sys-0.12.1/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/log-0.4.34/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/log-0.4.34/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/memchr-2.8.3/COPYING +3 -0
- package/LICENSES/dependencies/memchr-2.8.3/LICENSE-MIT +21 -0
- package/LICENSES/dependencies/memchr-2.8.3/UNLICENSE +24 -0
- package/LICENSES/dependencies/mio-1.2.3/LICENSE +19 -0
- package/LICENSES/dependencies/num-conv-0.2.2/LICENSE-Apache +177 -0
- package/LICENSES/dependencies/num-conv-0.2.2/LICENSE-MIT +19 -0
- package/LICENSES/dependencies/num-traits-0.2.19/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/num-traits-0.2.19/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/once_cell-1.21.4/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/once_cell-1.21.4/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/parking-2.2.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/parking-2.2.1/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/parking-2.2.1/LICENSE-THIRD-PARTY +9 -0
- package/LICENSES/dependencies/pin-project-1.1.13/LICENSE-APACHE +177 -0
- package/LICENSES/dependencies/pin-project-1.1.13/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/pin-project-internal-1.1.13/LICENSE-APACHE +177 -0
- package/LICENSES/dependencies/pin-project-internal-1.1.13/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/pin-project-lite-0.2.17/LICENSE-APACHE +177 -0
- package/LICENSES/dependencies/pin-project-lite-0.2.17/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/piper-0.2.5/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/piper-0.2.5/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/polling-3.11.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/polling-3.11.0/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/portable-atomic-1.15.0/LICENSE-APACHE +177 -0
- package/LICENSES/dependencies/portable-atomic-1.15.0/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/portable-atomic-util-0.2.8/LICENSE-APACHE +177 -0
- package/LICENSES/dependencies/portable-atomic-util-0.2.8/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/powerfmt-0.2.0/LICENSE-Apache +202 -0
- package/LICENSES/dependencies/powerfmt-0.2.0/LICENSE-MIT +19 -0
- package/LICENSES/dependencies/proc-macro2-1.0.107/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/proc-macro2-1.0.107/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/quote-1.0.47/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/quote-1.0.47/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/r-efi-6.0.0/AUTHORS +75 -0
- package/LICENSES/dependencies/rayon-1.12.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/rayon-1.12.0/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/rayon-core-1.13.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/rayon-core-1.13.0/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/ref-cast-1.0.27/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/ref-cast-1.0.27/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/ref-cast-impl-1.0.27/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/ref-cast-impl-1.0.27/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/rustc-demangle-0.1.28/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/rustc-demangle-0.1.28/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/rustc-hash-2.1.3/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/rustc-hash-2.1.3/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/rustc_version-0.4.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/rustc_version-0.4.1/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/rustix-1.1.4/COPYRIGHT +29 -0
- package/LICENSES/dependencies/rustix-1.1.4/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/rustix-1.1.4/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/LICENSES/dependencies/rustix-1.1.4/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/rustversion-1.0.23/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/rustversion-1.0.23/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/schemars-0.9.0/LICENSE +21 -0
- package/LICENSES/dependencies/schemars-1.2.2/LICENSE +21 -0
- package/LICENSES/dependencies/schemars_derive-1.2.2/LICENSE +21 -0
- package/LICENSES/dependencies/semver-1.0.28/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/semver-1.0.28/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/serde-1.0.229/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/serde-1.0.229/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/serde_core-1.0.229/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/serde_core-1.0.229/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/serde_derive-1.0.229/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/serde_derive-1.0.229/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/serde_derive_internals-0.30.0/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/serde_derive_internals-0.30.0/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/serde_json-1.0.151/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/serde_json-1.0.151/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/serde_with-3.23.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/serde_with-3.23.0/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/serde_with_macros-3.23.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/serde_with_macros-3.23.0/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/shell-words-1.1.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/shell-words-1.1.1/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/shlex-2.0.1/LICENSE-APACHE +13 -0
- package/LICENSES/dependencies/shlex-2.0.1/LICENSE-MIT +21 -0
- package/LICENSES/dependencies/signal-hook-registry-1.4.8/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/signal-hook-registry-1.4.8/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/slab-0.4.12/LICENSE +25 -0
- package/LICENSES/dependencies/smallvec-1.16.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/smallvec-1.16.0/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/socket2-0.6.5/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/socket2-0.6.5/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/stable_deref_trait-1.2.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/stable_deref_trait-1.2.1/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/strsim-0.11.1/LICENSE +23 -0
- package/LICENSES/dependencies/strum-0.28.0/LICENSE +21 -0
- package/LICENSES/dependencies/strum_macros-0.28.0/LICENSE +21 -0
- package/LICENSES/dependencies/syn-2.0.119/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/syn-2.0.119/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/syn-3.0.5/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/syn-3.0.5/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/thiserror-2.0.20/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/thiserror-2.0.20/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/thiserror-impl-2.0.20/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/thiserror-impl-2.0.20/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/time-0.3.55/LICENSE-Apache +177 -0
- package/LICENSES/dependencies/time-0.3.55/LICENSE-MIT +19 -0
- package/LICENSES/dependencies/time-core-0.1.9/LICENSE-Apache +177 -0
- package/LICENSES/dependencies/time-core-0.1.9/LICENSE-MIT +19 -0
- package/LICENSES/dependencies/time-macros-0.2.32/LICENSE-Apache +177 -0
- package/LICENSES/dependencies/time-macros-0.2.32/LICENSE-MIT +19 -0
- package/LICENSES/dependencies/tinyvec-1.13.2/LICENSE-APACHE.md +202 -0
- package/LICENSES/dependencies/tinyvec-1.13.2/LICENSE-MIT.md +5 -0
- package/LICENSES/dependencies/tinyvec-1.13.2/LICENSE-ZLIB.md +11 -0
- package/LICENSES/dependencies/tinyvec_macros-0.1.1/LICENSE-APACHE.md +202 -0
- package/LICENSES/dependencies/tinyvec_macros-0.1.1/LICENSE-MIT.md +21 -0
- package/LICENSES/dependencies/tinyvec_macros-0.1.1/LICENSE-ZLIB.md +20 -0
- package/LICENSES/dependencies/tokio-1.53.1/LICENSE +21 -0
- package/LICENSES/dependencies/tokio-macros-2.7.2/LICENSE +22 -0
- package/LICENSES/dependencies/tracing-0.1.44/LICENSE +25 -0
- package/LICENSES/dependencies/tracing-attributes-0.1.31/LICENSE +25 -0
- package/LICENSES/dependencies/tracing-core-0.1.36/LICENSE +25 -0
- package/LICENSES/dependencies/unicode-ident-1.0.24/LICENSE-APACHE +176 -0
- package/LICENSES/dependencies/unicode-ident-1.0.24/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/unicode-ident-1.0.24/LICENSE-UNICODE +39 -0
- package/LICENSES/dependencies/unicode-segmentation-1.13.3/COPYRIGHT +7 -0
- package/LICENSES/dependencies/unicode-segmentation-1.13.3/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/unicode-segmentation-1.13.3/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/unicode-xid-0.2.6/COPYRIGHT +7 -0
- package/LICENSES/dependencies/unicode-xid-0.2.6/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/unicode-xid-0.2.6/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/uuid-1.26.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/uuid-1.26.1/LICENSE-MIT +26 -0
- package/LICENSES/dependencies/walrus-0.26.5/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/walrus-0.26.5/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/walrus-macro-0.26.0/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/walrus-macro-0.26.0/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/LICENSES/dependencies/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/wasm-bindgen-0.2.128/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/wasm-bindgen-0.2.128/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/wasm-bindgen-cli-support-0.2.128/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/wasm-bindgen-cli-support-0.2.128/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/wasm-bindgen-macro-0.2.128/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/wasm-bindgen-macro-0.2.128/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/wasm-bindgen-macro-support-0.2.128/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/wasm-bindgen-macro-support-0.2.128/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/wasm-bindgen-shared-0.2.128/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/wasm-bindgen-shared-0.2.128/LICENSE-MIT +25 -0
- package/LICENSES/dependencies/wasm-encoder-0.245.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/wasm-encoder-0.245.1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/LICENSES/dependencies/wasm-encoder-0.245.1/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/wasmparser-0.245.1/LICENSE-APACHE +201 -0
- package/LICENSES/dependencies/wasmparser-0.245.1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/LICENSES/dependencies/wasmparser-0.245.1/LICENSE-MIT +23 -0
- package/LICENSES/dependencies/windows-core-0.62.2/license-apache-2.0 +201 -0
- package/LICENSES/dependencies/windows-core-0.62.2/license-mit +21 -0
- package/LICENSES/dependencies/windows-implement-0.60.2/license-apache-2.0 +201 -0
- package/LICENSES/dependencies/windows-implement-0.60.2/license-mit +21 -0
- package/LICENSES/dependencies/windows-interface-0.59.3/license-apache-2.0 +201 -0
- package/LICENSES/dependencies/windows-interface-0.59.3/license-mit +21 -0
- package/LICENSES/dependencies/windows-link-0.2.1/license-apache-2.0 +201 -0
- package/LICENSES/dependencies/windows-link-0.2.1/license-mit +21 -0
- package/LICENSES/dependencies/windows-result-0.4.1/license-apache-2.0 +201 -0
- package/LICENSES/dependencies/windows-result-0.4.1/license-mit +21 -0
- package/LICENSES/dependencies/windows-strings-0.5.1/license-apache-2.0 +201 -0
- package/LICENSES/dependencies/windows-strings-0.5.1/license-mit +21 -0
- package/LICENSES/dependencies/windows-sys-0.61.2/license-apache-2.0 +201 -0
- package/LICENSES/dependencies/windows-sys-0.61.2/license-mit +21 -0
- package/LICENSES/dependencies/zmij-1.0.23/LICENSE-MIT +23 -0
- package/THIRD-PARTY.md +62 -0
- package/package.json +37 -9
- package/source/.cargo/config.toml +5 -0
- package/source/.ci/upstream-notices/PROVENANCE.json +198 -0
- package/source/.ci/upstream-notices/agent-client-protocol-2.1.0/LICENSE +191 -0
- package/source/.ci/upstream-notices/agent-client-protocol-derive-2.1.0/LICENSE +191 -0
- package/source/.ci/upstream-notices/agent-client-protocol-schema-1.7.0/LICENSE +190 -0
- package/source/.ci/upstream-notices/defmt-parser-1.0.0/LICENSE-APACHE +201 -0
- package/source/.ci/upstream-notices/defmt-parser-1.0.0/LICENSE-MIT +25 -0
- package/source/.ci/upstream-notices/r-efi-6.0.0/AUTHORS +75 -0
- package/source/.ci/upstream-notices/wasm-encoder-0.245.1/LICENSE-APACHE +201 -0
- package/source/.ci/upstream-notices/wasm-encoder-0.245.1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/source/.ci/upstream-notices/wasm-encoder-0.245.1/LICENSE-MIT +23 -0
- package/source/.ci/upstream-notices/wasmparser-0.245.1/LICENSE-APACHE +201 -0
- package/source/.ci/upstream-notices/wasmparser-0.245.1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/source/.ci/upstream-notices/wasmparser-0.245.1/LICENSE-MIT +23 -0
- package/source/.ci/wasm-bundle/Cargo.toml +10 -0
- package/source/.ci/wasm-bundle/src/main.rs +17 -0
- package/source/CHANGELOG.md +85 -0
- package/source/Cargo.lock +1630 -0
- package/source/Cargo.toml +48 -0
- package/source/LICENSE +307 -0
- package/source/LICENSE.md +30 -0
- package/source/LICENSES/GPL-3.0-only.txt +232 -0
- package/source/LICENSES/LGPL-3.0-linking-exception.txt +16 -0
- package/source/LICENSES/LGPL-3.0-only WITH LGPL-3.0-linking-exception.txt +16 -0
- package/source/LICENSES/LGPL-3.0-only.txt +304 -0
- package/source/README.md +289 -0
- package/source/THIRD-PARTY.md +62 -0
- package/source/dependencies.tar.gz +0 -0
- package/source/examples/native.rs +45 -0
- package/source/src/auth.rs +435 -0
- package/source/src/configuration.rs +140 -0
- package/source/src/conversation.rs +594 -0
- package/source/src/dock.rs +564 -0
- package/source/src/lib.rs +39 -0
- package/source/src/native/client.rs +401 -0
- package/source/src/native/drivers/codex.rs +506 -0
- package/source/src/native/drivers/mod.rs +305 -0
- package/source/src/native/drivers/opencode.rs +531 -0
- package/source/src/native/env.rs +264 -0
- package/source/src/native/fixture.py +191 -0
- package/source/src/native/mod.rs +257 -0
- package/source/src/native/pool.rs +440 -0
- package/source/src/native/session.rs +609 -0
- package/source/src/native/tests.rs +667 -0
- package/source/src/transport.rs +330 -0
- package/source/src/web.rs +35 -0
- package/wasm/ccht_bg.wasm +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Third-party components
|
|
2
|
+
|
|
3
|
+
ccht uses the official `agent-client-protocol` Rust SDK and
|
|
4
|
+
`agent-client-protocol-schema` under Apache-2.0. `wasm-bindgen` and the generated
|
|
5
|
+
binding toolchain retain their MIT/Apache-2.0 grants. The exact locked dependency
|
|
6
|
+
inventory is generated by `.ci/license_inventory.py`; browser distributions carry
|
|
7
|
+
original dependency notices in `LICENSES/dependencies/`.
|
|
8
|
+
No Zed editor source is included. Zed's external-agent architecture is a reference;
|
|
9
|
+
its GPL editor code is not relicensed by ccht's LGPL grant.
|
|
10
|
+
|
|
11
|
+
External runtimes are separately supplied by the operator:
|
|
12
|
+
|
|
13
|
+
| Runtime | Source license / terms |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| Codex ACP adapter and Codex | Apache-2.0; OpenAI account/service terms remain applicable |
|
|
16
|
+
| OpenCode | MIT; configured provider/account terms remain applicable |
|
|
17
|
+
| Claude ACP adapter | Apache-2.0 wrapper; the Claude Agent SDK/runtime has separate proprietary Anthropic terms |
|
|
18
|
+
|
|
19
|
+
ccht does not redistribute these executables. Preserve their original notices and
|
|
20
|
+
comply with their runtime and service conditions when packaging them yourself.
|
|
21
|
+
Do not infer permission to offer a customer-facing subscription login or pooled
|
|
22
|
+
account service from the wrapper's permissive license.
|
|
23
|
+
|
|
24
|
+
Permissive references informing the native login drivers (no vendored code;
|
|
25
|
+
hand-rolled protocol shapes with attribution in the module docs):
|
|
26
|
+
|
|
27
|
+
- Codex app-server protocol (`account/login/start` device-code challenge,
|
|
28
|
+
`account/login/completed`, `account/read`) from openai/codex under
|
|
29
|
+
Apache-2.0 informs `src/native/drivers/codex.rs` (`CodexDeviceDriver`).
|
|
30
|
+
The driver spawns the caller's Codex `app-server`, relays the
|
|
31
|
+
host-allowlisted challenge, and projects presence only.
|
|
32
|
+
- OpenCode `serve` control plane (`GET /global/health`,
|
|
33
|
+
`PUT /auth/opencode-go`, `GET /provider`) under MIT informs
|
|
34
|
+
`src/native/drivers/opencode.rs` (`OpenCodeKeyDriver`). The driver uses
|
|
35
|
+
loopback `opencode serve` with an ephemeral port, an env-only server
|
|
36
|
+
password, and hand-rolled HTTP with no new dependencies.
|
|
37
|
+
|
|
38
|
+
Preserve their original grants and notices when reusing these shapes.
|
|
39
|
+
|
|
40
|
+
Upstream sources: [ACP SDK](https://github.com/agentclientprotocol/rust-sdk),
|
|
41
|
+
[Codex ACP](https://github.com/agentclientprotocol/codex-acp),
|
|
42
|
+
[openai/codex](https://github.com/openai/codex),
|
|
43
|
+
[Claude ACP](https://github.com/agentclientprotocol/claude-agent-acp),
|
|
44
|
+
[OpenCode](https://github.com/anomalyco/opencode),
|
|
45
|
+
[wasm-bindgen](https://github.com/wasm-bindgen/wasm-bindgen).
|
|
46
|
+
|
|
47
|
+
The published ACP SDK and derive 2.1.0, ACP schema 1.7.0, defmt-parser 1.0.0,
|
|
48
|
+
wasm-encoder 0.245.1, and wasmparser 0.245.1 archives omit their repositories'
|
|
49
|
+
root license files. Their unchanged original notices are retained under
|
|
50
|
+
`.ci/upstream-notices/<package>-<version>/` for inclusion in browser artifacts.
|
|
51
|
+
This preserves Zed's copyright notices, both defmt license alternatives, and all
|
|
52
|
+
three wasm-tools license alternatives, including the LLVM exception.
|
|
53
|
+
|
|
54
|
+
The r-efi 6.0.0 archive already includes its full MIT grant, copyright notices,
|
|
55
|
+
and Apache-2.0/LGPL-2.1-or-later alternative notices in `AUTHORS`. That unchanged
|
|
56
|
+
file is also retained as a supplemental notice because its filename is outside
|
|
57
|
+
the usual `LICENSE`/`NOTICE` convention. All these notices were verified against
|
|
58
|
+
the exact upstream revisions recorded in the published crates'
|
|
59
|
+
`.cargo_vcs_info.json`; their crate archive checksums match `Cargo.lock`.
|
|
60
|
+
[Notice provenance](.ci/upstream-notices/PROVENANCE.json) records immutable source
|
|
61
|
+
URLs, revisions, upstream paths, Git blob hashes, and SHA-256 hashes. No upstream
|
|
62
|
+
implementation source or license alternatives were modified.
|
|
Binary file
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//! Stream from an operator-supplied, already authenticated ACP executable.
|
|
2
|
+
//!
|
|
3
|
+
//! Example: cargo run --example native -- opencode acp
|
|
4
|
+
|
|
5
|
+
use ccht::native::{AgentCommand, NativeClient, NativeOptions, SessionOptions};
|
|
6
|
+
use ccht::{Event, Prompt, acp};
|
|
7
|
+
|
|
8
|
+
#[tokio::main]
|
|
9
|
+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
10
|
+
let mut arguments = std::env::args().skip(1);
|
|
11
|
+
let executable = arguments
|
|
12
|
+
.next()
|
|
13
|
+
.ok_or("supply an installed ACP executable")?;
|
|
14
|
+
let directory = std::env::current_dir()?;
|
|
15
|
+
let command = AgentCommand::new(executable)
|
|
16
|
+
.args(arguments)
|
|
17
|
+
.with_working_directory(&directory)?;
|
|
18
|
+
let client = NativeClient::connect(command, NativeOptions::default()).await?;
|
|
19
|
+
let mut session = client.new_session(SessionOptions::new(directory)).await?;
|
|
20
|
+
let handle = session.handle();
|
|
21
|
+
let mut events = session.take_events()?;
|
|
22
|
+
let reader = tokio::spawn(async move {
|
|
23
|
+
while let Some(message) = events.recv().await {
|
|
24
|
+
match message.event {
|
|
25
|
+
Event::Update {
|
|
26
|
+
update: acp::SessionUpdate::AgentMessageChunk(chunk),
|
|
27
|
+
} => {
|
|
28
|
+
if let acp::ContentBlock::Text(text) = chunk.content {
|
|
29
|
+
print!("{}", text.text);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
Event::Completed { .. } | Event::Error { .. } => break,
|
|
33
|
+
_ => {}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
let result = handle
|
|
38
|
+
.prompt(Prompt::text("example-turn", "Say hello in one sentence."))
|
|
39
|
+
.await;
|
|
40
|
+
client.close().await?;
|
|
41
|
+
reader.await?;
|
|
42
|
+
result?;
|
|
43
|
+
println!();
|
|
44
|
+
Ok(())
|
|
45
|
+
}
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
//! Provider login state and credential-store abstraction.
|
|
2
|
+
//!
|
|
3
|
+
//! Structural inspiration comes from the provider/auth split in Zed
|
|
4
|
+
//! (GPL-3.0): uniform per-provider auth state, an application-supplied
|
|
5
|
+
//! credential store, and key lifecycle with environment override. This
|
|
6
|
+
//! module is reimplemented from scratch for LGPL use; no Zed code is
|
|
7
|
+
//! contained here.
|
|
8
|
+
//!
|
|
9
|
+
//! Boundary, unchanged: the store sees opaque bytes keyed by service URL
|
|
10
|
+
//! and never learns what they unlock; applications own login ceremonies,
|
|
11
|
+
//! UI, and where the store persists (OS keychain natively, browser storage
|
|
12
|
+
//! on the web). Nothing here performs network I/O.
|
|
13
|
+
|
|
14
|
+
use std::collections::HashMap;
|
|
15
|
+
use std::future::Future;
|
|
16
|
+
use std::pin::Pin;
|
|
17
|
+
use std::sync::Mutex;
|
|
18
|
+
|
|
19
|
+
use serde::{Deserialize, Serialize};
|
|
20
|
+
|
|
21
|
+
/// Login state of one provider as observed by the application.
|
|
22
|
+
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
|
23
|
+
#[serde(rename_all = "snake_case")]
|
|
24
|
+
pub enum AuthState {
|
|
25
|
+
/// Not checked yet (for example, before the store was read).
|
|
26
|
+
#[default]
|
|
27
|
+
Unknown,
|
|
28
|
+
/// Credentials exist. The account label is display-only provenance and
|
|
29
|
+
/// must never be treated as an authentication proof by itself.
|
|
30
|
+
Authenticated {
|
|
31
|
+
/// Display-only account label (username, email, or subscription name).
|
|
32
|
+
account: Option<String>,
|
|
33
|
+
},
|
|
34
|
+
/// No credentials and no other evidence of a login.
|
|
35
|
+
Unauthenticated,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
impl AuthState {
|
|
39
|
+
/// Whether routine work may proceed without prompting for login first.
|
|
40
|
+
#[must_use]
|
|
41
|
+
pub fn authenticated(&self) -> bool {
|
|
42
|
+
matches!(self, Self::Authenticated { .. })
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Storage failure of a [`CredentialsProvider`].
|
|
47
|
+
#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
|
|
48
|
+
pub enum AuthError {
|
|
49
|
+
/// The backing store failed (keychain locked, disk full, quota hit).
|
|
50
|
+
#[error("credential store failed: {0}")]
|
|
51
|
+
Store(String),
|
|
52
|
+
/// The operation makes no sense for this store (for example, writing to
|
|
53
|
+
/// a read-only native-agent home owned by the agent itself).
|
|
54
|
+
#[error("operation not supported by this credential store")]
|
|
55
|
+
NotSupported,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Opaque credential: an account label plus secret bytes.
|
|
59
|
+
///
|
|
60
|
+
/// Stores must treat both fields as opaque. The label exists so UIs can say
|
|
61
|
+
/// *who* is signed in without ever unlocking the secret.
|
|
62
|
+
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
|
63
|
+
pub struct Credential {
|
|
64
|
+
/// Display-only account label (username, email, or subscription name).
|
|
65
|
+
pub account: String,
|
|
66
|
+
/// Secret bytes. Never logged, never embedded in errors or fixtures.
|
|
67
|
+
pub secret: Vec<u8>,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// Application-supplied credential persistence.
|
|
71
|
+
///
|
|
72
|
+
/// One method triple per service URL. Native applications back this with the
|
|
73
|
+
/// OS keychain; web applications with origin-scoped browser storage; tests
|
|
74
|
+
/// with [`MemoryCredentialsProvider`].
|
|
75
|
+
pub trait CredentialsProvider: Send + Sync {
|
|
76
|
+
/// Read stored credentials, if any, for a service URL.
|
|
77
|
+
fn read_credentials<'a>(
|
|
78
|
+
&'a self,
|
|
79
|
+
service: &'a str,
|
|
80
|
+
) -> Pin<Box<dyn Future<Output = Result<Option<Credential>, AuthError>> + Send + 'a>>;
|
|
81
|
+
|
|
82
|
+
/// Persist credentials for a service URL, replacing any previous entry.
|
|
83
|
+
fn write_credentials<'a>(
|
|
84
|
+
&'a self,
|
|
85
|
+
service: &'a str,
|
|
86
|
+
credential: &'a Credential,
|
|
87
|
+
) -> Pin<Box<dyn Future<Output = Result<(), AuthError>> + Send + 'a>>;
|
|
88
|
+
|
|
89
|
+
/// Remove stored credentials for a service URL. Missing entries are not
|
|
90
|
+
/// an error.
|
|
91
|
+
fn delete_credentials<'a>(
|
|
92
|
+
&'a self,
|
|
93
|
+
service: &'a str,
|
|
94
|
+
) -> Pin<Box<dyn Future<Output = Result<(), AuthError>> + Send + 'a>>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// In-memory [`CredentialsProvider`] for tests and local development.
|
|
98
|
+
///
|
|
99
|
+
/// Never ships credentials anywhere; drop it when the test ends.
|
|
100
|
+
#[derive(Debug, Default)]
|
|
101
|
+
pub struct MemoryCredentialsProvider {
|
|
102
|
+
entries: Mutex<HashMap<String, Credential>>,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
impl MemoryCredentialsProvider {
|
|
106
|
+
/// Empty store.
|
|
107
|
+
#[must_use]
|
|
108
|
+
pub fn new() -> Self {
|
|
109
|
+
Self::default()
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
impl CredentialsProvider for MemoryCredentialsProvider {
|
|
114
|
+
fn read_credentials<'a>(
|
|
115
|
+
&'a self,
|
|
116
|
+
service: &'a str,
|
|
117
|
+
) -> Pin<Box<dyn Future<Output = Result<Option<Credential>, AuthError>> + Send + 'a>> {
|
|
118
|
+
let found = match self.entries.lock() {
|
|
119
|
+
Ok(entries) => entries.get(service).cloned(),
|
|
120
|
+
Err(_) => {
|
|
121
|
+
return Box::pin(async move { Err(AuthError::Store("lock poisoned".into())) });
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
Box::pin(async move { Ok(found) })
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fn write_credentials<'a>(
|
|
128
|
+
&'a self,
|
|
129
|
+
service: &'a str,
|
|
130
|
+
credential: &'a Credential,
|
|
131
|
+
) -> Pin<Box<dyn Future<Output = Result<(), AuthError>> + Send + 'a>> {
|
|
132
|
+
let result = self
|
|
133
|
+
.entries
|
|
134
|
+
.lock()
|
|
135
|
+
.map(|mut entries| {
|
|
136
|
+
entries.insert(service.to_owned(), credential.clone());
|
|
137
|
+
})
|
|
138
|
+
.map_err(|_| AuthError::Store("lock poisoned".into()));
|
|
139
|
+
Box::pin(async move { result })
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fn delete_credentials<'a>(
|
|
143
|
+
&'a self,
|
|
144
|
+
service: &'a str,
|
|
145
|
+
) -> Pin<Box<dyn Future<Output = Result<(), AuthError>> + Send + 'a>> {
|
|
146
|
+
let result = self
|
|
147
|
+
.entries
|
|
148
|
+
.lock()
|
|
149
|
+
.map(|mut entries| {
|
|
150
|
+
entries.remove(service);
|
|
151
|
+
})
|
|
152
|
+
.map_err(|_| AuthError::Store("lock poisoned".into()));
|
|
153
|
+
Box::pin(async move { result })
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/// Lifecycle of one provider's key: environment override wins, otherwise the
|
|
158
|
+
/// store decides. Mirrors the semantics applications need for a Zed-style
|
|
159
|
+
/// settings surface: `is_authenticated()` for badges, `store()` for the
|
|
160
|
+
/// save action, `reset()` for sign-out, `load_if_needed()` for lazy boot.
|
|
161
|
+
#[derive(Clone, Debug, Default)]
|
|
162
|
+
pub struct ApiKeyState {
|
|
163
|
+
key: Option<String>,
|
|
164
|
+
from_env_var: bool,
|
|
165
|
+
// The environment override is unavailable on Wasm; the field is still set
|
|
166
|
+
// through `new` on every target so construction stays uniform.
|
|
167
|
+
#[cfg_attr(target_family = "wasm", allow(dead_code))]
|
|
168
|
+
env_var_name: Option<&'static str>,
|
|
169
|
+
loaded_service: Option<String>,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
impl ApiKeyState {
|
|
173
|
+
/// Unloaded state, optionally honoring an environment variable override.
|
|
174
|
+
#[must_use]
|
|
175
|
+
pub fn new(env_var_name: Option<&'static str>) -> Self {
|
|
176
|
+
Self {
|
|
177
|
+
env_var_name,
|
|
178
|
+
..Self::default()
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// Whether routine work may proceed: an in-memory key is present.
|
|
183
|
+
#[must_use]
|
|
184
|
+
pub fn has_key(&self) -> bool {
|
|
185
|
+
self.key.as_deref().is_some_and(|key| !key.is_empty())
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// Whether the current key came from the environment (read-only: reset
|
|
189
|
+
/// is disabled and the UI must say so instead of failing silently).
|
|
190
|
+
#[must_use]
|
|
191
|
+
pub fn is_from_env_var(&self) -> bool {
|
|
192
|
+
self.from_env_var
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/// The in-memory key, if any. Callers forward it to request signing;
|
|
196
|
+
/// they never persist or display it.
|
|
197
|
+
#[must_use]
|
|
198
|
+
pub fn key(&self) -> Option<&str> {
|
|
199
|
+
self.key.as_deref()
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// Load from environment or store unless this exact service already was.
|
|
203
|
+
/// Returns the resulting [`AuthState`].
|
|
204
|
+
///
|
|
205
|
+
/// The environment override is unavailable on Wasm (browsers expose no
|
|
206
|
+
/// process environment); there the store alone decides.
|
|
207
|
+
pub async fn load_if_needed(
|
|
208
|
+
&mut self,
|
|
209
|
+
service: &str,
|
|
210
|
+
store: &(dyn CredentialsProvider + Send + Sync),
|
|
211
|
+
) -> Result<AuthState, AuthError> {
|
|
212
|
+
if self.loaded_service.as_deref() == Some(service) {
|
|
213
|
+
return Ok(self.snapshot());
|
|
214
|
+
}
|
|
215
|
+
#[cfg(not(target_family = "wasm"))]
|
|
216
|
+
if let Some(name) = self.env_var_name
|
|
217
|
+
&& let Ok(value) = std::env::var(name)
|
|
218
|
+
&& !value.trim().is_empty()
|
|
219
|
+
{
|
|
220
|
+
self.key = Some(value);
|
|
221
|
+
self.from_env_var = true;
|
|
222
|
+
self.loaded_service = Some(service.to_owned());
|
|
223
|
+
return Ok(self.snapshot());
|
|
224
|
+
}
|
|
225
|
+
self.from_env_var = false;
|
|
226
|
+
self.key = store
|
|
227
|
+
.read_credentials(service)
|
|
228
|
+
.await?
|
|
229
|
+
.and_then(|credential| {
|
|
230
|
+
String::from_utf8(credential.secret)
|
|
231
|
+
.ok()
|
|
232
|
+
.filter(|secret| !secret.trim().is_empty())
|
|
233
|
+
});
|
|
234
|
+
self.loaded_service = Some(service.to_owned());
|
|
235
|
+
Ok(self.snapshot())
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/// Forget a service change: the next `load_if_needed` reads again.
|
|
239
|
+
pub fn handle_service_change(&mut self, service: &str) {
|
|
240
|
+
if self.loaded_service.as_deref() != Some(service) {
|
|
241
|
+
self.key = None;
|
|
242
|
+
self.from_env_var = false;
|
|
243
|
+
self.loaded_service = None;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/// Persist a key (or `None` to sign out) through the store. Refuses while
|
|
248
|
+
/// the key comes from the environment: callers must unset the variable.
|
|
249
|
+
pub async fn store(
|
|
250
|
+
&mut self,
|
|
251
|
+
service: &str,
|
|
252
|
+
key: Option<String>,
|
|
253
|
+
store: &(dyn CredentialsProvider + Send + Sync),
|
|
254
|
+
) -> Result<AuthState, AuthError> {
|
|
255
|
+
if self.from_env_var {
|
|
256
|
+
return Err(AuthError::NotSupported);
|
|
257
|
+
}
|
|
258
|
+
match key
|
|
259
|
+
.map(|key| key.trim().to_owned())
|
|
260
|
+
.filter(|key| !key.is_empty())
|
|
261
|
+
{
|
|
262
|
+
Some(key) => {
|
|
263
|
+
store
|
|
264
|
+
.write_credentials(
|
|
265
|
+
service,
|
|
266
|
+
&Credential {
|
|
267
|
+
account: String::new(),
|
|
268
|
+
secret: key.into_bytes(),
|
|
269
|
+
},
|
|
270
|
+
)
|
|
271
|
+
.await?;
|
|
272
|
+
self.key = store
|
|
273
|
+
.read_credentials(service)
|
|
274
|
+
.await?
|
|
275
|
+
.and_then(|credential| String::from_utf8(credential.secret).ok());
|
|
276
|
+
}
|
|
277
|
+
None => {
|
|
278
|
+
store.delete_credentials(service).await?;
|
|
279
|
+
self.key = None;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
self.loaded_service = Some(service.to_owned());
|
|
283
|
+
Ok(self.snapshot())
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
fn snapshot(&self) -> AuthState {
|
|
287
|
+
if self.has_key() {
|
|
288
|
+
AuthState::Authenticated { account: None }
|
|
289
|
+
} else {
|
|
290
|
+
AuthState::Unauthenticated
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
#[cfg(test)]
|
|
296
|
+
mod tests {
|
|
297
|
+
use super::*;
|
|
298
|
+
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
|
299
|
+
|
|
300
|
+
/// Minimal single-threaded executor so core tests need no async runtime.
|
|
301
|
+
fn block_on<F: Future>(mut future: F) -> F::Output {
|
|
302
|
+
// SAFETY: all waker callbacks ignore the null data pointer.
|
|
303
|
+
unsafe fn clone_raw(_: *const ()) -> RawWaker {
|
|
304
|
+
RawWaker::new(std::ptr::null(), &VTABLE)
|
|
305
|
+
}
|
|
306
|
+
unsafe fn noop_raw(_: *const ()) {}
|
|
307
|
+
static VTABLE: RawWakerVTable =
|
|
308
|
+
RawWakerVTable::new(clone_raw, noop_raw, noop_raw, noop_raw);
|
|
309
|
+
// SAFETY: the waker never dereferences its null data pointer.
|
|
310
|
+
let waker = unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), &VTABLE)) };
|
|
311
|
+
let mut context = Context::from_waker(&waker);
|
|
312
|
+
// SAFETY: the future is never moved after pinning for the poll loop.
|
|
313
|
+
let mut future = unsafe { Pin::new_unchecked(&mut future) };
|
|
314
|
+
loop {
|
|
315
|
+
match future.as_mut().poll(&mut context) {
|
|
316
|
+
Poll::Ready(output) => return output,
|
|
317
|
+
Poll::Pending => std::thread::yield_now(),
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async fn roundtrip(store: &MemoryCredentialsProvider) {
|
|
323
|
+
let credential = Credential {
|
|
324
|
+
account: "Pro".into(),
|
|
325
|
+
secret: b"secret".to_vec(),
|
|
326
|
+
};
|
|
327
|
+
assert_eq!(store.read_credentials("svc").await.unwrap(), None);
|
|
328
|
+
store.write_credentials("svc", &credential).await.unwrap();
|
|
329
|
+
assert_eq!(
|
|
330
|
+
store.read_credentials("svc").await.unwrap(),
|
|
331
|
+
Some(credential)
|
|
332
|
+
);
|
|
333
|
+
store.delete_credentials("svc").await.unwrap();
|
|
334
|
+
assert_eq!(store.read_credentials("svc").await.unwrap(), None);
|
|
335
|
+
// Deleting a missing entry is not an error.
|
|
336
|
+
store.delete_credentials("svc").await.unwrap();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
#[test]
|
|
340
|
+
fn memory_store_roundtrips() {
|
|
341
|
+
block_on(roundtrip(&MemoryCredentialsProvider::new()));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
#[test]
|
|
345
|
+
fn api_key_state_starts_unauthenticated() {
|
|
346
|
+
let state = ApiKeyState::new(None);
|
|
347
|
+
assert!(!state.has_key());
|
|
348
|
+
assert!(!state.is_from_env_var());
|
|
349
|
+
assert_eq!(state.snapshot(), AuthState::Unauthenticated);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
#[test]
|
|
353
|
+
fn api_key_state_save_and_sign_out() {
|
|
354
|
+
block_on(async {
|
|
355
|
+
let store = MemoryCredentialsProvider::new();
|
|
356
|
+
let mut state = ApiKeyState::new(None);
|
|
357
|
+
let seen = state.load_if_needed("svc", &store).await.unwrap();
|
|
358
|
+
assert_eq!(seen, AuthState::Unauthenticated);
|
|
359
|
+
let seen = state
|
|
360
|
+
.store("svc", Some(" key ".into()), &store)
|
|
361
|
+
.await
|
|
362
|
+
.unwrap();
|
|
363
|
+
assert_eq!(seen, AuthState::Authenticated { account: None });
|
|
364
|
+
assert!(state.has_key());
|
|
365
|
+
// Blank input signs out instead of storing whitespace.
|
|
366
|
+
let seen = state
|
|
367
|
+
.store("svc", Some(" ".into()), &store)
|
|
368
|
+
.await
|
|
369
|
+
.unwrap();
|
|
370
|
+
assert_eq!(seen, AuthState::Unauthenticated);
|
|
371
|
+
assert!(!state.has_key());
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
#[cfg(not(target_family = "wasm"))]
|
|
376
|
+
#[test]
|
|
377
|
+
fn api_key_state_prefers_environment_and_locks_reset() {
|
|
378
|
+
block_on(async {
|
|
379
|
+
// SAFETY: single-threaded test with no other environment readers.
|
|
380
|
+
unsafe { std::env::set_var("CCHT_TEST_API_KEY", "env-key") };
|
|
381
|
+
let store = MemoryCredentialsProvider::new();
|
|
382
|
+
let mut state = ApiKeyState::new(Some("CCHT_TEST_API_KEY"));
|
|
383
|
+
let seen = state.load_if_needed("svc", &store).await.unwrap();
|
|
384
|
+
assert_eq!(seen, AuthState::Authenticated { account: None });
|
|
385
|
+
assert!(state.is_from_env_var());
|
|
386
|
+
assert_eq!(
|
|
387
|
+
state.store("svc", None, &store).await.unwrap_err(),
|
|
388
|
+
AuthError::NotSupported
|
|
389
|
+
);
|
|
390
|
+
// SAFETY: restores the pre-test environment; see above.
|
|
391
|
+
unsafe { std::env::remove_var("CCHT_TEST_API_KEY") };
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
#[test]
|
|
396
|
+
fn api_key_state_reloads_on_service_change() {
|
|
397
|
+
block_on(async {
|
|
398
|
+
let store = MemoryCredentialsProvider::new();
|
|
399
|
+
let mut state = ApiKeyState::new(None);
|
|
400
|
+
state
|
|
401
|
+
.store("a", Some("key-a".into()), &store)
|
|
402
|
+
.await
|
|
403
|
+
.unwrap();
|
|
404
|
+
assert!(state.has_key());
|
|
405
|
+
state.handle_service_change("b");
|
|
406
|
+
assert!(!state.has_key());
|
|
407
|
+
let seen = state.load_if_needed("b", &store).await.unwrap();
|
|
408
|
+
assert_eq!(seen, AuthState::Unauthenticated);
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
#[test]
|
|
413
|
+
fn api_key_state_keeps_key_for_same_service() {
|
|
414
|
+
block_on(async {
|
|
415
|
+
let store = MemoryCredentialsProvider::new();
|
|
416
|
+
let mut state = ApiKeyState::new(None);
|
|
417
|
+
state
|
|
418
|
+
.store("a", Some("key-a".into()), &store)
|
|
419
|
+
.await
|
|
420
|
+
.unwrap();
|
|
421
|
+
state.handle_service_change("a");
|
|
422
|
+
assert!(state.has_key());
|
|
423
|
+
// A repeated load for the same service returns the cached snapshot.
|
|
424
|
+
let seen = state.load_if_needed("a", &store).await.unwrap();
|
|
425
|
+
assert_eq!(seen, AuthState::Authenticated { account: None });
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
#[test]
|
|
430
|
+
fn auth_state_reports_readiness() {
|
|
431
|
+
assert!(!AuthState::Unknown.authenticated());
|
|
432
|
+
assert!(!AuthState::Unauthenticated.authenticated());
|
|
433
|
+
assert!(AuthState::Authenticated { account: None }.authenticated());
|
|
434
|
+
}
|
|
435
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
//! Agent-owned session controls shared by native, Wasm and transport consumers.
|
|
2
|
+
|
|
3
|
+
use serde::{Deserialize, Serialize};
|
|
4
|
+
|
|
5
|
+
use crate::acp::{
|
|
6
|
+
SessionConfigKind, SessionConfigOption, SessionConfigOptionCategory, SessionConfigOptionValue,
|
|
7
|
+
SessionConfigSelectOptions, SessionModeState, SessionUpdate,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/// An agent's current advertised configuration; option order is significant.
|
|
11
|
+
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
12
|
+
pub struct SessionConfiguration {
|
|
13
|
+
/// Model, mode, reasoning and other controls supplied by the agent.
|
|
14
|
+
pub options: Vec<SessionConfigOption>,
|
|
15
|
+
/// Legacy modes for agents that have not adopted configuration options.
|
|
16
|
+
pub modes: Option<SessionModeState>,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
impl SessionConfiguration {
|
|
20
|
+
/// The first model selector, following the agent's priority ordering.
|
|
21
|
+
pub fn model_option(&self) -> Option<&SessionConfigOption> {
|
|
22
|
+
self.options.iter().find(|option| {
|
|
23
|
+
option.category == Some(SessionConfigOptionCategory::Model)
|
|
24
|
+
|| option.id.0.as_ref() == "model"
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Whether an exact value is offered, including grouped selectors and booleans.
|
|
29
|
+
pub fn accepts(&self, id: &str, value: &SessionConfigOptionValue) -> bool {
|
|
30
|
+
let Some(option) = self
|
|
31
|
+
.options
|
|
32
|
+
.iter()
|
|
33
|
+
.find(|option| option.id.0.as_ref() == id)
|
|
34
|
+
else {
|
|
35
|
+
return false;
|
|
36
|
+
};
|
|
37
|
+
match (&option.kind, value) {
|
|
38
|
+
(SessionConfigKind::Boolean(_), SessionConfigOptionValue::Boolean { .. }) => true,
|
|
39
|
+
(SessionConfigKind::Select(select), SessionConfigOptionValue::ValueId { value }) => {
|
|
40
|
+
match &select.options {
|
|
41
|
+
SessionConfigSelectOptions::Ungrouped(options) => {
|
|
42
|
+
options.iter().any(|option| option.value == *value)
|
|
43
|
+
}
|
|
44
|
+
SessionConfigSelectOptions::Grouped(groups) => groups
|
|
45
|
+
.iter()
|
|
46
|
+
.any(|group| group.options.iter().any(|option| option.value == *value)),
|
|
47
|
+
_ => false,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
_ => false,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Apply an agent update. Configuration updates replace the complete option list.
|
|
55
|
+
pub fn apply(&mut self, update: &SessionUpdate) {
|
|
56
|
+
match update {
|
|
57
|
+
SessionUpdate::ConfigOptionUpdate(update) => {
|
|
58
|
+
self.options.clone_from(&update.config_options);
|
|
59
|
+
}
|
|
60
|
+
SessionUpdate::CurrentModeUpdate(update) => {
|
|
61
|
+
if let Some(modes) = &mut self.modes {
|
|
62
|
+
modes.current_mode_id.clone_from(&update.current_mode_id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
_ => {}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[cfg(test)]
|
|
71
|
+
mod tests {
|
|
72
|
+
use super::*;
|
|
73
|
+
#[test]
|
|
74
|
+
fn grouped_selectors_and_boolean_controls_preserve_exact_types() {
|
|
75
|
+
let settings: SessionConfiguration = serde_json::from_value(serde_json::json!({
|
|
76
|
+
"options": [{"id":"model","name":"Model","category":"model","type":"select",
|
|
77
|
+
"currentValue":"one","options":[{"group":"provider","name":"Provider","options":[
|
|
78
|
+
{"value":"one","name":"One"},{"value":"two","name":"Two"}]}]},
|
|
79
|
+
{"id":"thinking","name":"Thinking","type":"boolean","currentValue":false}],
|
|
80
|
+
"modes":null
|
|
81
|
+
}))
|
|
82
|
+
.unwrap();
|
|
83
|
+
assert!(settings.accepts("model", &"two".into()));
|
|
84
|
+
assert!(!settings.accepts("model", &true.into()));
|
|
85
|
+
assert!(settings.accepts("thinking", &true.into()));
|
|
86
|
+
assert!(!settings.accepts("thinking", &"true".into()));
|
|
87
|
+
assert!(!settings.accepts("missing", &true.into()));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#[test]
|
|
91
|
+
fn model_option_is_none_without_advertised_model() {
|
|
92
|
+
assert!(SessionConfiguration::default().model_option().is_none());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
#[test]
|
|
96
|
+
fn apply_replaces_options_and_tracks_current_mode() {
|
|
97
|
+
use crate::acp::{
|
|
98
|
+
ConfigOptionUpdate, ContentChunk, CurrentModeUpdate, SessionModeId, SessionModeState,
|
|
99
|
+
};
|
|
100
|
+
let mut settings = SessionConfiguration::default();
|
|
101
|
+
// Other updates leave the configuration unchanged.
|
|
102
|
+
settings.apply(&SessionUpdate::AgentMessageChunk(ContentChunk::new(
|
|
103
|
+
"hi".into(),
|
|
104
|
+
)));
|
|
105
|
+
assert!(settings.options.is_empty());
|
|
106
|
+
|
|
107
|
+
let options: SessionConfiguration = serde_json::from_value(serde_json::json!({
|
|
108
|
+
"options": [{"id":"model","name":"Model","type":"select",
|
|
109
|
+
"currentValue":"one","options":[{"value":"one","name":"One"}]}],
|
|
110
|
+
"modes": null
|
|
111
|
+
}))
|
|
112
|
+
.unwrap();
|
|
113
|
+
settings.apply(&SessionUpdate::ConfigOptionUpdate(ConfigOptionUpdate::new(
|
|
114
|
+
options.options.clone(),
|
|
115
|
+
)));
|
|
116
|
+
assert_eq!(settings.options, options.options);
|
|
117
|
+
assert!(settings.model_option().is_some());
|
|
118
|
+
|
|
119
|
+
// Without legacy modes, a mode update is a no-op.
|
|
120
|
+
settings.apply(&SessionUpdate::CurrentModeUpdate(CurrentModeUpdate::new(
|
|
121
|
+
SessionModeId::new("second"),
|
|
122
|
+
)));
|
|
123
|
+
assert!(settings.modes.is_none());
|
|
124
|
+
|
|
125
|
+
settings.modes = Some(SessionModeState::new(
|
|
126
|
+
SessionModeId::new("first"),
|
|
127
|
+
Vec::new(),
|
|
128
|
+
));
|
|
129
|
+
settings.apply(&SessionUpdate::CurrentModeUpdate(CurrentModeUpdate::new(
|
|
130
|
+
SessionModeId::new("second"),
|
|
131
|
+
)));
|
|
132
|
+
assert_eq!(
|
|
133
|
+
settings
|
|
134
|
+
.modes
|
|
135
|
+
.as_ref()
|
|
136
|
+
.map(|modes| modes.current_mode_id.0.as_ref()),
|
|
137
|
+
Some("second")
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|